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

Commit 61283ecc authored by Makoto Onuki's avatar Makoto Onuki
Browse files

Exempt sync requests by FG app from app-standby

Test: manual test with request sync, etc
Bug: 72443754
Change-Id: Iecf2d3a8c54451324a02ca2762bda72aa219bd92
parent 2ef26bf2
Loading
Loading
Loading
Loading
+28 −1
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.commands.requestsync;


import android.accounts.Account;
import android.accounts.Account;
import android.content.ContentResolver;
import android.content.ContentResolver;
import android.content.SyncRequest;
import android.os.Bundle;
import android.os.Bundle;


import java.net.URISyntaxException;
import java.net.URISyntaxException;
@@ -28,12 +29,31 @@ public class RequestSync {
    private String[] mArgs;
    private String[] mArgs;
    private int mNextArg;
    private int mNextArg;
    private String mCurArgData;
    private String mCurArgData;
    private boolean mIsForegroundRequest;


    enum Operation {
    enum Operation {
        REQUEST_SYNC {
        REQUEST_SYNC {
            @Override
            @Override
            void invoke(RequestSync caller) {
            void invoke(RequestSync caller) {
                ContentResolver.requestSync(caller.mAccount, caller.mAuthority, caller.mExtras);
                if (caller.mIsForegroundRequest) {
                    caller.mExtras.putBoolean(
                            ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC, true);
                } else {
                    caller.mExtras.putBoolean(
                            ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC, true);
                    System.out.println(
                            "Making a sync request as a background app.\n"
                            + "Note: request may be throttled by App Standby.\n"
                            + "To override this behavior and run a sync immediately,"
                            + " pass a -f option.\n");
                }
                final SyncRequest request =
                        new SyncRequest.Builder()
                                .setSyncAdapter(caller.mAccount, caller.mAuthority)
                                .setExtras(caller.mExtras)
                                .syncOnce()
                                .build();
                ContentResolver.requestSync(request);
            }
            }
        },
        },
        ADD_PERIODIC_SYNC {
        ADD_PERIODIC_SYNC {
@@ -191,6 +211,10 @@ public class RequestSync {
                final String key = nextArgRequired();
                final String key = nextArgRequired();
                final String value = nextArgRequired();
                final String value = nextArgRequired();
                mExtras.putBoolean(key, Boolean.valueOf(value));
                mExtras.putBoolean(key, Boolean.valueOf(value));

            } else if (opt.equals("-f") || opt.equals("--foreground")) {
                mIsForegroundRequest = true;

            } else {
            } else {
                System.err.println("Error: Unknown option: " + opt);
                System.err.println("Error: Unknown option: " + opt);
                showUsage();
                showUsage();
@@ -267,6 +291,9 @@ public class RequestSync {
                "       -n|--account-name <ACCOUNT-NAME>\n" +
                "       -n|--account-name <ACCOUNT-NAME>\n" +
                "       -t|--account-type <ACCOUNT-TYPE>\n" +
                "       -t|--account-type <ACCOUNT-TYPE>\n" +
                "       -a|--authority <AUTHORITY>\n" +
                "       -a|--authority <AUTHORITY>\n" +
                "    App-standby related options\n" +
                "\n" +
                "       -f|--foreground (Exempt a sync from app standby)\n" +
                "    ContentResolver extra options:\n" +
                "    ContentResolver extra options:\n" +
                "      --is|--ignore-settings: Add SYNC_EXTRAS_IGNORE_SETTINGS\n" +
                "      --is|--ignore-settings: Add SYNC_EXTRAS_IGNORE_SETTINGS\n" +
                "      --ib|--ignore-backoff: Add SYNC_EXTRAS_IGNORE_BACKOFF\n" +
                "      --ib|--ignore-backoff: Add SYNC_EXTRAS_IGNORE_BACKOFF\n" +
+21 −7
Original line number Original line Diff line number Diff line
@@ -165,6 +165,26 @@ public abstract class ContentResolver {
    /** {@hide} Flag to allow sync to occur on metered network. */
    /** {@hide} Flag to allow sync to occur on metered network. */
    public static final String SYNC_EXTRAS_DISALLOW_METERED = "allow_metered";
    public static final String SYNC_EXTRAS_DISALLOW_METERED = "allow_metered";


    /**
     * {@hide} Flag only used by the requestsync command to treat a request as if it was made by
     * a foreground app.
     *
     * Only the system and the shell user can set it.
     *
     * This extra is "virtual". Once passed to the system server, it'll be removed from the bundle.
     */
    public static final String SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC = "force_fg_sync";

    /**
     * {@hide} Flag only used by the requestsync command to treat a request as if it was made by
     * a background app.
     *
     * Only the system and the shell user can set it.
     *
     * This extra is "virtual". Once passed to the system server, it'll be removed from the bundle.
     */
    public static final String SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC = "force_bg_sync";

    /**
    /**
     * Set by the SyncManager to request that the SyncAdapter initialize itself for
     * Set by the SyncManager to request that the SyncAdapter initialize itself for
     * the given account/authority pair. One required initialization step is to
     * the given account/authority pair. One required initialization step is to
@@ -2435,13 +2455,7 @@ public abstract class ContentResolver {
    public static void addPeriodicSync(Account account, String authority, Bundle extras,
    public static void addPeriodicSync(Account account, String authority, Bundle extras,
            long pollFrequency) {
            long pollFrequency) {
        validateSyncExtrasBundle(extras);
        validateSyncExtrasBundle(extras);
        if (extras.getBoolean(SYNC_EXTRAS_MANUAL, false)
        if (invalidPeriodicExtras(extras)) {
                || extras.getBoolean(SYNC_EXTRAS_DO_NOT_RETRY, false)
                || extras.getBoolean(SYNC_EXTRAS_IGNORE_BACKOFF, false)
                || extras.getBoolean(SYNC_EXTRAS_IGNORE_SETTINGS, false)
                || extras.getBoolean(SYNC_EXTRAS_INITIALIZE, false)
                || extras.getBoolean(SYNC_EXTRAS_FORCE, false)
                || extras.getBoolean(SYNC_EXTRAS_EXPEDITED, false)) {
            throw new IllegalArgumentException("illegal extras were set");
            throw new IllegalArgumentException("illegal extras were set");
        }
        }
        try {
        try {
+73 −7
Original line number Original line Diff line number Diff line
@@ -48,8 +48,8 @@ import android.os.Bundle;
import android.os.FactoryTest;
import android.os.FactoryTest;
import android.os.IBinder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserHandle;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArrayMap;
@@ -446,7 +446,7 @@ public final class ContentService extends IContentService.Stub {
                SyncManager syncManager = getSyncManager();
                SyncManager syncManager = getSyncManager();
                if (syncManager != null) {
                if (syncManager != null) {
                    syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
                    syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
                            uri.getAuthority());
                            uri.getAuthority(), /*isAppStandbyExempted=*/ isUidInForeground(uid));
                }
                }
            }
            }


@@ -502,6 +502,9 @@ public final class ContentService extends IContentService.Stub {
        int userId = UserHandle.getCallingUserId();
        int userId = UserHandle.getCallingUserId();
        int uId = Binder.getCallingUid();
        int uId = Binder.getCallingUid();


        validateExtras(uId, extras);
        final boolean isForegroundSyncRequest = isForegroundSyncRequest(uId, extras);

        // This makes it so that future permission checks will be in the context of this
        // This makes it so that future permission checks will be in the context of this
        // process rather than the caller's process. We will restore this before returning.
        // process rather than the caller's process. We will restore this before returning.
        long identityToken = clearCallingIdentity();
        long identityToken = clearCallingIdentity();
@@ -509,7 +512,8 @@ public final class ContentService extends IContentService.Stub {
            SyncManager syncManager = getSyncManager();
            SyncManager syncManager = getSyncManager();
            if (syncManager != null) {
            if (syncManager != null) {
                syncManager.scheduleSync(account, userId, uId, authority, extras,
                syncManager.scheduleSync(account, userId, uId, authority, extras,
                        SyncStorageEngine.AuthorityInfo.UNDEFINED);
                        SyncStorageEngine.AuthorityInfo.UNDEFINED,
                        /*isAppStandbyExempted=*/ isForegroundSyncRequest);
            }
            }
        } finally {
        } finally {
            restoreCallingIdentity(identityToken);
            restoreCallingIdentity(identityToken);
@@ -548,6 +552,12 @@ public final class ContentService extends IContentService.Stub {
    public void syncAsUser(SyncRequest request, int userId) {
    public void syncAsUser(SyncRequest request, int userId) {
        enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId);
        enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId);
        int callerUid = Binder.getCallingUid();
        int callerUid = Binder.getCallingUid();

        final Bundle extras = request.getBundle();

        validateExtras(callerUid, extras);
        final boolean isForegroundSyncRequest = isForegroundSyncRequest(callerUid, extras);

        // This makes it so that future permission checks will be in the context of this
        // This makes it so that future permission checks will be in the context of this
        // process rather than the caller's process. We will restore this before returning.
        // process rather than the caller's process. We will restore this before returning.
        long identityToken = clearCallingIdentity();
        long identityToken = clearCallingIdentity();
@@ -556,8 +566,6 @@ public final class ContentService extends IContentService.Stub {
            if (syncManager == null) {
            if (syncManager == null) {
                return;
                return;
            }
            }

            Bundle extras = request.getBundle();
            long flextime = request.getSyncFlexTime();
            long flextime = request.getSyncFlexTime();
            long runAtTime = request.getSyncRunTime();
            long runAtTime = request.getSyncRunTime();
            if (request.isPeriodic()) {
            if (request.isPeriodic()) {
@@ -575,7 +583,8 @@ public final class ContentService extends IContentService.Stub {
            } else {
            } else {
                syncManager.scheduleSync(
                syncManager.scheduleSync(
                        request.getAccount(), userId, callerUid, request.getProvider(), extras,
                        request.getAccount(), userId, callerUid, request.getProvider(), extras,
                        SyncStorageEngine.AuthorityInfo.UNDEFINED);
                        SyncStorageEngine.AuthorityInfo.UNDEFINED,
                        /*isAppStandbyExempted=*/ isForegroundSyncRequest);
            }
            }
        } finally {
        } finally {
            restoreCallingIdentity(identityToken);
            restoreCallingIdentity(identityToken);
@@ -649,10 +658,13 @@ public final class ContentService extends IContentService.Stub {
                    "no permission to write the sync settings");
                    "no permission to write the sync settings");
        }
        }


        Bundle extras = new Bundle(request.getBundle());
        validateExtras(callingUid, extras);

        long identityToken = clearCallingIdentity();
        long identityToken = clearCallingIdentity();
        try {
        try {
            SyncStorageEngine.EndPoint info;
            SyncStorageEngine.EndPoint info;
            Bundle extras = new Bundle(request.getBundle());

            Account account = request.getAccount();
            Account account = request.getAccount();
            String provider = request.getProvider();
            String provider = request.getProvider();
            info = new SyncStorageEngine.EndPoint(account, provider, userId);
            info = new SyncStorageEngine.EndPoint(account, provider, userId);
@@ -787,6 +799,8 @@ public final class ContentService extends IContentService.Stub {
        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                "no permission to write the sync settings");
                "no permission to write the sync settings");


        validateExtras(Binder.getCallingUid(), extras);

        int userId = UserHandle.getCallingUserId();
        int userId = UserHandle.getCallingUserId();


        pollFrequency = clampPeriod(pollFrequency);
        pollFrequency = clampPeriod(pollFrequency);
@@ -815,6 +829,8 @@ public final class ContentService extends IContentService.Stub {
        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                "no permission to write the sync settings");
                "no permission to write the sync settings");


        validateExtras(Binder.getCallingUid(), extras);

        final int callingUid = Binder.getCallingUid();
        final int callingUid = Binder.getCallingUid();


        int userId = UserHandle.getCallingUserId();
        int userId = UserHandle.getCallingUserId();
@@ -1239,6 +1255,56 @@ public final class ContentService extends IContentService.Stub {
        return SyncStorageEngine.AuthorityInfo.UNDEFINED;
        return SyncStorageEngine.AuthorityInfo.UNDEFINED;
    }
    }


    private void validateExtras(int callingUid, Bundle extras) {
        if (extras.containsKey(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC)
                || extras.containsKey(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC)
                ) {
            switch (callingUid) {
                case Process.ROOT_UID:
                case Process.SHELL_UID:
                case Process.SYSTEM_UID:
                    break; // Okay
                default:
                    throw new SecurityException("Invalid extras specified.");
            }
        }
    }

    private boolean isForegroundSyncRequest(int callingUid, Bundle extras) {
        final boolean isForegroundRequest;
        if (extras.getBoolean(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC)) {
            isForegroundRequest = true;
        } else if (extras.getBoolean(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC)) {
            isForegroundRequest = false;
        } else {
            isForegroundRequest = isUidInForeground(callingUid);
        }
        extras.remove(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC);
        extras.remove(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC);

        return isForegroundRequest;
    }

    private boolean isUidInForeground(int uid) {
        // If the caller is ADB, we assume it's a background request by default, because
        // that's also the default of requests from the requestsync command.
        // The requestsync command will always set either SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC or
        // SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC (for non-periodic sync requests),
        // so it shouldn't matter in practice.
        switch (uid) {
            case Process.SHELL_UID:
            case Process.ROOT_UID:
                return false;
        }
        final ActivityManagerInternal ami =
                LocalServices.getService(ActivityManagerInternal.class);
        if (ami != null) {
            return ami.getUidProcessState(uid)
                    <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
        }
        return false;
    }

    /**
    /**
     * Hide this class since it is not part of api,
     * Hide this class since it is not part of api,
     * but current unittest framework requires it to be public
     * but current unittest framework requires it to be public
+78 −25
Original line number Original line Diff line number Diff line
@@ -576,9 +576,10 @@ public class SyncManager {
        mSyncStorageEngine = SyncStorageEngine.getSingleton();
        mSyncStorageEngine = SyncStorageEngine.getSingleton();
        mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
        mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
            @Override
            @Override
            public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras) {
            public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras,
                    boolean isAppStandbyExempted) {
                scheduleSync(info.account, info.userId, reason, info.provider, extras,
                scheduleSync(info.account, info.userId, reason, info.provider, extras,
                        AuthorityInfo.UNDEFINED);
                        AuthorityInfo.UNDEFINED, isAppStandbyExempted);
            }
            }
        });
        });


@@ -608,7 +609,8 @@ public class SyncManager {
                if (!removed) {
                if (!removed) {
                    scheduleSync(null, UserHandle.USER_ALL,
                    scheduleSync(null, UserHandle.USER_ALL,
                            SyncOperation.REASON_SERVICE_CHANGED,
                            SyncOperation.REASON_SERVICE_CHANGED,
                            type.authority, null, AuthorityInfo.UNDEFINED);
                            type.authority, null, AuthorityInfo.UNDEFINED,
                            /*isAppStandbyExempted=*/ false);
                }
                }
            }
            }
        }, mSyncHandler);
        }, mSyncHandler);
@@ -656,7 +658,8 @@ public class SyncManager {
            if (mAccountManagerInternal.hasAccountAccess(account, uid)) {
            if (mAccountManagerInternal.hasAccountAccess(account, uid)) {
                scheduleSync(account, UserHandle.getUserId(uid),
                scheduleSync(account, UserHandle.getUserId(uid),
                        SyncOperation.REASON_ACCOUNTS_UPDATED,
                        SyncOperation.REASON_ACCOUNTS_UPDATED,
                        null, null, AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS);
                        null, null, AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS,
                        /*isAppStandbyExempted=*/ false);
            }
            }
        });
        });


@@ -881,9 +884,10 @@ public class SyncManager {
     *           Use {@link AuthorityInfo#UNDEFINED} to sync all authorities.
     *           Use {@link AuthorityInfo#UNDEFINED} to sync all authorities.
     */
     */
    public void scheduleSync(Account requestedAccount, int userId, int reason,
    public void scheduleSync(Account requestedAccount, int userId, int reason,
                             String requestedAuthority, Bundle extras, int targetSyncState) {
            String requestedAuthority, Bundle extras, int targetSyncState,
            boolean isAppStandbyExempted) {
        scheduleSync(requestedAccount, userId, reason, requestedAuthority, extras, targetSyncState,
        scheduleSync(requestedAccount, userId, reason, requestedAuthority, extras, targetSyncState,
                0 /* min delay */, true /* checkIfAccountReady */);
                0 /* min delay */, true /* checkIfAccountReady */, isAppStandbyExempted);
    }
    }


    /**
    /**
@@ -891,7 +895,8 @@ public class SyncManager {
     */
     */
    private void scheduleSync(Account requestedAccount, int userId, int reason,
    private void scheduleSync(Account requestedAccount, int userId, int reason,
            String requestedAuthority, Bundle extras, int targetSyncState,
            String requestedAuthority, Bundle extras, int targetSyncState,
                             final long minDelayMillis, boolean checkIfAccountReady) {
            final long minDelayMillis, boolean checkIfAccountReady,
            boolean isAppStandbyExempted) {
        final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
        final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
        if (extras == null) {
        if (extras == null) {
            extras = new Bundle();
            extras = new Bundle();
@@ -1009,7 +1014,8 @@ public class SyncManager {
                                        && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
                                        && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
                                    scheduleSync(account.account, userId, reason, authority,
                                    scheduleSync(account.account, userId, reason, authority,
                                            finalExtras, targetSyncState, minDelayMillis,
                                            finalExtras, targetSyncState, minDelayMillis,
                                            true /* checkIfAccountReady */);
                                            true /* checkIfAccountReady */,
                                            isAppStandbyExempted);
                                }
                                }
                            }
                            }
                        ));
                        ));
@@ -1060,7 +1066,7 @@ public class SyncManager {
                        sendOnUnsyncableAccount(mContext, syncAdapterInfo, account.userId,
                        sendOnUnsyncableAccount(mContext, syncAdapterInfo, account.userId,
                                () -> scheduleSync(account.account, account.userId, reason,
                                () -> scheduleSync(account.account, account.userId, reason,
                                        authority, finalExtras, targetSyncState, minDelayMillis,
                                        authority, finalExtras, targetSyncState, minDelayMillis,
                                        false));
                                        false, isAppStandbyExempted));
                    } else {
                    } else {
                        // Initialisation sync.
                        // Initialisation sync.
                        Bundle newExtras = new Bundle();
                        Bundle newExtras = new Bundle();
@@ -1078,7 +1084,8 @@ public class SyncManager {
                        postScheduleSyncMessage(
                        postScheduleSyncMessage(
                                new SyncOperation(account.account, account.userId,
                                new SyncOperation(account.account, account.userId,
                                        owningUid, owningPackage, reason, source,
                                        owningUid, owningPackage, reason, source,
                                        authority, newExtras, allowParallelSyncs),
                                        authority, newExtras, allowParallelSyncs,
                                        isAppStandbyExempted),
                                minDelayMillis
                                minDelayMillis
                        );
                        );
                    }
                    }
@@ -1095,7 +1102,7 @@ public class SyncManager {
                    postScheduleSyncMessage(
                    postScheduleSyncMessage(
                            new SyncOperation(account.account, account.userId,
                            new SyncOperation(account.account, account.userId,
                                    owningUid, owningPackage, reason, source,
                                    owningUid, owningPackage, reason, source,
                                    authority, extras, allowParallelSyncs),
                                    authority, extras, allowParallelSyncs, isAppStandbyExempted),
                            minDelayMillis
                            minDelayMillis
                    );
                    );
                }
                }
@@ -1208,11 +1215,13 @@ public class SyncManager {
     * Schedule sync based on local changes to a provider. We wait for at least LOCAL_SYNC_DELAY
     * Schedule sync based on local changes to a provider. We wait for at least LOCAL_SYNC_DELAY
     * ms to batch syncs.
     * ms to batch syncs.
     */
     */
    public void scheduleLocalSync(Account account, int userId, int reason, String authority) {
    public void scheduleLocalSync(Account account, int userId, int reason, String authority,
            boolean isAppStandbyExempted) {
        final Bundle extras = new Bundle();
        final Bundle extras = new Bundle();
        extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
        extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
        scheduleSync(account, userId, reason, authority, extras,
        scheduleSync(account, userId, reason, authority, extras,
                AuthorityInfo.UNDEFINED, LOCAL_SYNC_DELAY, true /* checkIfAccountReady */);
                AuthorityInfo.UNDEFINED, LOCAL_SYNC_DELAY, true /* checkIfAccountReady */,
                isAppStandbyExempted);
    }
    }


    public SyncAdapterType[] getSyncAdapterTypes(int userId) {
    public SyncAdapterType[] getSyncAdapterTypes(int userId) {
@@ -1480,7 +1489,11 @@ public class SyncManager {
        }
        }


        // Check if duplicate syncs are pending. If found, keep one with least expected run time.
        // Check if duplicate syncs are pending. If found, keep one with least expected run time.

        // If any of the duplicate ones has exemption, then we inherit it.
        if (!syncOperation.isPeriodic) {
        if (!syncOperation.isPeriodic) {
            boolean inheritAppStandbyExemption = false;

            // Check currently running syncs
            // Check currently running syncs
            for (ActiveSyncContext asc: mActiveSyncContexts) {
            for (ActiveSyncContext asc: mActiveSyncContexts) {
                if (asc.mSyncOperation.key.equals(syncOperation.key)) {
                if (asc.mSyncOperation.key.equals(syncOperation.key)) {
@@ -1496,14 +1509,14 @@ public class SyncManager {
            long now = SystemClock.elapsedRealtime();
            long now = SystemClock.elapsedRealtime();
            syncOperation.expectedRuntime = now + minDelay;
            syncOperation.expectedRuntime = now + minDelay;
            List<SyncOperation> pending = getAllPendingSyncs();
            List<SyncOperation> pending = getAllPendingSyncs();
            SyncOperation opWithLeastExpectedRuntime = syncOperation;
            SyncOperation syncToRun = syncOperation;
            for (SyncOperation op : pending) {
            for (SyncOperation op : pending) {
                if (op.isPeriodic) {
                if (op.isPeriodic) {
                    continue;
                    continue;
                }
                }
                if (op.key.equals(syncOperation.key)) {
                if (op.key.equals(syncOperation.key)) {
                    if (opWithLeastExpectedRuntime.expectedRuntime > op.expectedRuntime) {
                    if (syncToRun.expectedRuntime > op.expectedRuntime) {
                        opWithLeastExpectedRuntime = op;
                        syncToRun = op;
                    }
                    }
                    duplicatesCount++;
                    duplicatesCount++;
                }
                }
@@ -1511,26 +1524,54 @@ public class SyncManager {
            if (duplicatesCount > 1) {
            if (duplicatesCount > 1) {
                Slog.e(TAG, "FATAL ERROR! File a bug if you see this.");
                Slog.e(TAG, "FATAL ERROR! File a bug if you see this.");
            }
            }

            if (syncOperation != syncToRun) {
                // If there's a duplicate with an earlier run time that's not exempted,
                // and if the current operation is exempted with no minDelay,
                // cancel the duplicate one and keep the current one.
                //
                // This means the duplicate one has a negative expected run time, but it hasn't
                // been executed possibly because of app-standby.

                if (syncOperation.isAppStandbyExempted
                        && (minDelay == 0)
                        && !syncToRun.isAppStandbyExempted) {
                    syncToRun = syncOperation;
                }
            }

            // Cancel all other duplicate syncs.
            for (SyncOperation op : pending) {
            for (SyncOperation op : pending) {
                if (op.isPeriodic) {
                if (op.isPeriodic) {
                    continue;
                    continue;
                }
                }
                if (op.key.equals(syncOperation.key)) {
                if (op.key.equals(syncOperation.key)) {
                    if (op != opWithLeastExpectedRuntime) {
                    if (op != syncToRun) {
                        if (isLoggable) {
                        if (isLoggable) {
                            Slog.v(TAG, "Cancelling duplicate sync " + op);
                            Slog.v(TAG, "Cancelling duplicate sync " + op);
                        }
                        }
                        if (op.isAppStandbyExempted) {
                            inheritAppStandbyExemption = true;
                        }
                        cancelJob(op, "scheduleSyncOperationH-duplicate");
                        cancelJob(op, "scheduleSyncOperationH-duplicate");
                    }
                    }
                }
                }
            }
            }
            if (opWithLeastExpectedRuntime != syncOperation) {
            if (syncToRun != syncOperation) {
                // Don't schedule because a duplicate sync with earlier expected runtime exists.
                // Don't schedule because a duplicate sync with earlier expected runtime exists.
                if (isLoggable) {
                if (isLoggable) {
                    Slog.v(TAG, "Not scheduling because a duplicate exists.");
                    Slog.v(TAG, "Not scheduling because a duplicate exists.");
                }
                }

                // TODO Should we give the winning one SYNC_EXTRAS_APP_STANDBY_EXEMPTED
                // if the current one has it?
                return;
                return;
            }
            }

            // If any of the duplicates had exemption, we exempt the current one.
            if (inheritAppStandbyExemption) {
                syncOperation.isAppStandbyExempted = true;
            }
        }
        }


        // Syncs that are re-scheduled shouldn't get a new job id.
        // Syncs that are re-scheduled shouldn't get a new job id.
@@ -1547,12 +1588,18 @@ public class SyncManager {
        final int networkType = syncOperation.isNotAllowedOnMetered() ?
        final int networkType = syncOperation.isNotAllowedOnMetered() ?
                JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;
                JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;


        // Note this logic means when an exempted sync fails,
        // the back-off one will inherit it too, and will be exempted from app-standby.
        final int jobFlags = syncOperation.isAppStandbyExempted
                ? JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY : 0;

        JobInfo.Builder b = new JobInfo.Builder(syncOperation.jobId,
        JobInfo.Builder b = new JobInfo.Builder(syncOperation.jobId,
                new ComponentName(mContext, SyncJobService.class))
                new ComponentName(mContext, SyncJobService.class))
                .setExtras(syncOperation.toJobInfoExtras())
                .setExtras(syncOperation.toJobInfoExtras())
                .setRequiredNetworkType(networkType)
                .setRequiredNetworkType(networkType)
                .setPersisted(true)
                .setPersisted(true)
                .setPriority(priority);
                .setPriority(priority)
                .setFlags(jobFlags);


        if (syncOperation.isPeriodic) {
        if (syncOperation.isPeriodic) {
            b.setPeriodic(syncOperation.periodMillis, syncOperation.flexMillis);
            b.setPeriodic(syncOperation.periodMillis, syncOperation.flexMillis);
@@ -1683,12 +1730,12 @@ public class SyncManager {
        EndPoint target = new EndPoint(null, null, userId);
        EndPoint target = new EndPoint(null, null, userId);
        updateRunningAccounts(target);
        updateRunningAccounts(target);


        // Schedule sync for any accounts under started user.
        // Schedule sync for any accounts under started user, but only the NOT_INITIALIZED adapters.
        final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId,
        final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId,
                mContext.getOpPackageName());
                mContext.getOpPackageName());
        for (Account account : accounts) {
        for (Account account : accounts) {
            scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
            scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
                    AuthorityInfo.NOT_INITIALIZED);
                    AuthorityInfo.NOT_INITIALIZED, /*isAppStandbyExempted=*/ false);
        }
        }
    }
    }


@@ -3144,7 +3191,8 @@ public class SyncManager {
            if (syncTargets != null) {
            if (syncTargets != null) {
                scheduleSync(syncTargets.account, syncTargets.userId,
                scheduleSync(syncTargets.account, syncTargets.userId,
                        SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider,
                        SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider,
                null, AuthorityInfo.NOT_INITIALIZED);
                        null, AuthorityInfo.NOT_INITIALIZED,
                        /*isAppStandbyExempted=*/ false);
            }
            }
        }
        }


@@ -3211,7 +3259,7 @@ public class SyncManager {
                    syncAdapterInfo.componentName.getPackageName(), SyncOperation.REASON_PERIODIC,
                    syncAdapterInfo.componentName.getPackageName(), SyncOperation.REASON_PERIODIC,
                    SyncStorageEngine.SOURCE_PERIODIC, extras,
                    SyncStorageEngine.SOURCE_PERIODIC, extras,
                    syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID,
                    syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID,
                    pollFrequencyMillis, flexMillis);
                    pollFrequencyMillis, flexMillis, /*isAppStandbyExempted=*/ false);


            final int syncOpState = computeSyncOpState(op);
            final int syncOpState = computeSyncOpState(op);
            switch (syncOpState) {
            switch (syncOpState) {
@@ -3590,7 +3638,8 @@ public class SyncManager {
                                syncOperation.owningUid, syncOperation.owningPackage,
                                syncOperation.owningUid, syncOperation.owningPackage,
                                syncOperation.reason,
                                syncOperation.reason,
                                syncOperation.syncSource, info.provider, new Bundle(),
                                syncOperation.syncSource, info.provider, new Bundle(),
                                syncOperation.allowParallelSyncs));
                                syncOperation.allowParallelSyncs,
                                syncOperation.isAppStandbyExempted));
            }
            }
        }
        }


@@ -3808,6 +3857,10 @@ public class SyncManager {
        if (key.equals(ContentResolver.SYNC_EXTRAS_INITIALIZE)) {
        if (key.equals(ContentResolver.SYNC_EXTRAS_INITIALIZE)) {
            return true;
            return true;
        }
        }
//        if (key.equals(ContentResolver.SYNC_EXTRAS_APP_STANDBY_EXEMPTED)) {
//            return true;
//        }
        // No need to check virtual flags such as SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC.
        return false;
        return false;
    }
    }


+21 −8

File changed.

Preview size limit exceeded, changes collapsed.

Loading