Loading cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java +28 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.commands.requestsync; import android.accounts.Account; import android.content.ContentResolver; import android.content.SyncRequest; import android.os.Bundle; import java.net.URISyntaxException; Loading @@ -28,12 +29,31 @@ public class RequestSync { private String[] mArgs; private int mNextArg; private String mCurArgData; private boolean mIsForegroundRequest; enum Operation { REQUEST_SYNC { @Override 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 { Loading Loading @@ -191,6 +211,10 @@ public class RequestSync { final String key = nextArgRequired(); final String value = nextArgRequired(); mExtras.putBoolean(key, Boolean.valueOf(value)); } else if (opt.equals("-f") || opt.equals("--foreground")) { mIsForegroundRequest = true; } else { System.err.println("Error: Unknown option: " + opt); showUsage(); Loading Loading @@ -267,6 +291,9 @@ public class RequestSync { " -n|--account-name <ACCOUNT-NAME>\n" + " -t|--account-type <ACCOUNT-TYPE>\n" + " -a|--authority <AUTHORITY>\n" + " App-standby related options\n" + "\n" + " -f|--foreground (Exempt a sync from app standby)\n" + " ContentResolver extra options:\n" + " --is|--ignore-settings: Add SYNC_EXTRAS_IGNORE_SETTINGS\n" + " --ib|--ignore-backoff: Add SYNC_EXTRAS_IGNORE_BACKOFF\n" + Loading core/java/android/content/ContentResolver.java +21 −7 Original line number Diff line number Diff line Loading @@ -165,6 +165,26 @@ public abstract class ContentResolver { /** {@hide} Flag to allow sync to occur on metered network. */ 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 * the given account/authority pair. One required initialization step is to Loading Loading @@ -2435,13 +2455,7 @@ public abstract class ContentResolver { public static void addPeriodicSync(Account account, String authority, Bundle extras, long pollFrequency) { validateSyncExtrasBundle(extras); if (extras.getBoolean(SYNC_EXTRAS_MANUAL, false) || 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)) { if (invalidPeriodicExtras(extras)) { throw new IllegalArgumentException("illegal extras were set"); } try { Loading services/core/java/com/android/server/content/ContentService.java +73 −7 Original line number Diff line number Diff line Loading @@ -48,8 +48,8 @@ import android.os.Bundle; import android.os.FactoryTest; import android.os.IBinder; import android.os.Parcel; import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; import android.text.TextUtils; import android.util.ArrayMap; Loading Loading @@ -446,7 +446,7 @@ public final class ContentService extends IContentService.Stub { SyncManager syncManager = getSyncManager(); if (syncManager != null) { syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid, uri.getAuthority()); uri.getAuthority(), /*isAppStandbyExempted=*/ isUidInForeground(uid)); } } Loading Loading @@ -502,6 +502,9 @@ public final class ContentService extends IContentService.Stub { int userId = UserHandle.getCallingUserId(); 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 // process rather than the caller's process. We will restore this before returning. long identityToken = clearCallingIdentity(); Loading @@ -509,7 +512,8 @@ public final class ContentService extends IContentService.Stub { SyncManager syncManager = getSyncManager(); if (syncManager != null) { syncManager.scheduleSync(account, userId, uId, authority, extras, SyncStorageEngine.AuthorityInfo.UNDEFINED); SyncStorageEngine.AuthorityInfo.UNDEFINED, /*isAppStandbyExempted=*/ isForegroundSyncRequest); } } finally { restoreCallingIdentity(identityToken); Loading Loading @@ -548,6 +552,12 @@ public final class ContentService extends IContentService.Stub { public void syncAsUser(SyncRequest request, int userId) { enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId); 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 // process rather than the caller's process. We will restore this before returning. long identityToken = clearCallingIdentity(); Loading @@ -556,8 +566,6 @@ public final class ContentService extends IContentService.Stub { if (syncManager == null) { return; } Bundle extras = request.getBundle(); long flextime = request.getSyncFlexTime(); long runAtTime = request.getSyncRunTime(); if (request.isPeriodic()) { Loading @@ -575,7 +583,8 @@ public final class ContentService extends IContentService.Stub { } else { syncManager.scheduleSync( request.getAccount(), userId, callerUid, request.getProvider(), extras, SyncStorageEngine.AuthorityInfo.UNDEFINED); SyncStorageEngine.AuthorityInfo.UNDEFINED, /*isAppStandbyExempted=*/ isForegroundSyncRequest); } } finally { restoreCallingIdentity(identityToken); Loading Loading @@ -649,10 +658,13 @@ public final class ContentService extends IContentService.Stub { "no permission to write the sync settings"); } Bundle extras = new Bundle(request.getBundle()); validateExtras(callingUid, extras); long identityToken = clearCallingIdentity(); try { SyncStorageEngine.EndPoint info; Bundle extras = new Bundle(request.getBundle()); Account account = request.getAccount(); String provider = request.getProvider(); info = new SyncStorageEngine.EndPoint(account, provider, userId); Loading Loading @@ -787,6 +799,8 @@ public final class ContentService extends IContentService.Stub { mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, "no permission to write the sync settings"); validateExtras(Binder.getCallingUid(), extras); int userId = UserHandle.getCallingUserId(); pollFrequency = clampPeriod(pollFrequency); Loading Loading @@ -815,6 +829,8 @@ public final class ContentService extends IContentService.Stub { mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, "no permission to write the sync settings"); validateExtras(Binder.getCallingUid(), extras); final int callingUid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); Loading Loading @@ -1239,6 +1255,56 @@ public final class ContentService extends IContentService.Stub { 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, * but current unittest framework requires it to be public Loading services/core/java/com/android/server/content/SyncManager.java +78 −25 Original line number Diff line number Diff line Loading @@ -576,9 +576,10 @@ public class SyncManager { mSyncStorageEngine = SyncStorageEngine.getSingleton(); mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() { @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, AuthorityInfo.UNDEFINED); AuthorityInfo.UNDEFINED, isAppStandbyExempted); } }); Loading Loading @@ -608,7 +609,8 @@ public class SyncManager { if (!removed) { scheduleSync(null, UserHandle.USER_ALL, SyncOperation.REASON_SERVICE_CHANGED, type.authority, null, AuthorityInfo.UNDEFINED); type.authority, null, AuthorityInfo.UNDEFINED, /*isAppStandbyExempted=*/ false); } } }, mSyncHandler); Loading Loading @@ -656,7 +658,8 @@ public class SyncManager { if (mAccountManagerInternal.hasAccountAccess(account, uid)) { scheduleSync(account, UserHandle.getUserId(uid), SyncOperation.REASON_ACCOUNTS_UPDATED, null, null, AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS); null, null, AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS, /*isAppStandbyExempted=*/ false); } }); Loading Loading @@ -881,9 +884,10 @@ public class SyncManager { * Use {@link AuthorityInfo#UNDEFINED} to sync all authorities. */ 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, 0 /* min delay */, true /* checkIfAccountReady */); 0 /* min delay */, true /* checkIfAccountReady */, isAppStandbyExempted); } /** Loading @@ -891,7 +895,8 @@ public class SyncManager { */ private void scheduleSync(Account requestedAccount, int userId, int reason, 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); if (extras == null) { extras = new Bundle(); Loading Loading @@ -1009,7 +1014,8 @@ public class SyncManager { && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) { scheduleSync(account.account, userId, reason, authority, finalExtras, targetSyncState, minDelayMillis, true /* checkIfAccountReady */); true /* checkIfAccountReady */, isAppStandbyExempted); } } )); Loading Loading @@ -1060,7 +1066,7 @@ public class SyncManager { sendOnUnsyncableAccount(mContext, syncAdapterInfo, account.userId, () -> scheduleSync(account.account, account.userId, reason, authority, finalExtras, targetSyncState, minDelayMillis, false)); false, isAppStandbyExempted)); } else { // Initialisation sync. Bundle newExtras = new Bundle(); Loading @@ -1078,7 +1084,8 @@ public class SyncManager { postScheduleSyncMessage( new SyncOperation(account.account, account.userId, owningUid, owningPackage, reason, source, authority, newExtras, allowParallelSyncs), authority, newExtras, allowParallelSyncs, isAppStandbyExempted), minDelayMillis ); } Loading @@ -1095,7 +1102,7 @@ public class SyncManager { postScheduleSyncMessage( new SyncOperation(account.account, account.userId, owningUid, owningPackage, reason, source, authority, extras, allowParallelSyncs), authority, extras, allowParallelSyncs, isAppStandbyExempted), minDelayMillis ); } Loading Loading @@ -1208,11 +1215,13 @@ public class SyncManager { * Schedule sync based on local changes to a provider. We wait for at least LOCAL_SYNC_DELAY * 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(); extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); 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) { Loading Loading @@ -1480,7 +1489,11 @@ public class SyncManager { } // 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) { boolean inheritAppStandbyExemption = false; // Check currently running syncs for (ActiveSyncContext asc: mActiveSyncContexts) { if (asc.mSyncOperation.key.equals(syncOperation.key)) { Loading @@ -1496,14 +1509,14 @@ public class SyncManager { long now = SystemClock.elapsedRealtime(); syncOperation.expectedRuntime = now + minDelay; List<SyncOperation> pending = getAllPendingSyncs(); SyncOperation opWithLeastExpectedRuntime = syncOperation; SyncOperation syncToRun = syncOperation; for (SyncOperation op : pending) { if (op.isPeriodic) { continue; } if (op.key.equals(syncOperation.key)) { if (opWithLeastExpectedRuntime.expectedRuntime > op.expectedRuntime) { opWithLeastExpectedRuntime = op; if (syncToRun.expectedRuntime > op.expectedRuntime) { syncToRun = op; } duplicatesCount++; } Loading @@ -1511,26 +1524,54 @@ public class SyncManager { if (duplicatesCount > 1) { 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) { if (op.isPeriodic) { continue; } if (op.key.equals(syncOperation.key)) { if (op != opWithLeastExpectedRuntime) { if (op != syncToRun) { if (isLoggable) { Slog.v(TAG, "Cancelling duplicate sync " + op); } if (op.isAppStandbyExempted) { inheritAppStandbyExemption = true; } cancelJob(op, "scheduleSyncOperationH-duplicate"); } } } if (opWithLeastExpectedRuntime != syncOperation) { if (syncToRun != syncOperation) { // Don't schedule because a duplicate sync with earlier expected runtime exists. if (isLoggable) { 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; } // 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. Loading @@ -1547,12 +1588,18 @@ public class SyncManager { final int networkType = syncOperation.isNotAllowedOnMetered() ? 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, new ComponentName(mContext, SyncJobService.class)) .setExtras(syncOperation.toJobInfoExtras()) .setRequiredNetworkType(networkType) .setPersisted(true) .setPriority(priority); .setPriority(priority) .setFlags(jobFlags); if (syncOperation.isPeriodic) { b.setPeriodic(syncOperation.periodMillis, syncOperation.flexMillis); Loading Loading @@ -1683,12 +1730,12 @@ public class SyncManager { EndPoint target = new EndPoint(null, null, userId); 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, mContext.getOpPackageName()); for (Account account : accounts) { scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null, AuthorityInfo.NOT_INITIALIZED); AuthorityInfo.NOT_INITIALIZED, /*isAppStandbyExempted=*/ false); } } Loading Loading @@ -3144,7 +3191,8 @@ public class SyncManager { if (syncTargets != null) { scheduleSync(syncTargets.account, syncTargets.userId, SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider, null, AuthorityInfo.NOT_INITIALIZED); null, AuthorityInfo.NOT_INITIALIZED, /*isAppStandbyExempted=*/ false); } } Loading Loading @@ -3211,7 +3259,7 @@ public class SyncManager { syncAdapterInfo.componentName.getPackageName(), SyncOperation.REASON_PERIODIC, SyncStorageEngine.SOURCE_PERIODIC, extras, syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID, pollFrequencyMillis, flexMillis); pollFrequencyMillis, flexMillis, /*isAppStandbyExempted=*/ false); final int syncOpState = computeSyncOpState(op); switch (syncOpState) { Loading Loading @@ -3590,7 +3638,8 @@ public class SyncManager { syncOperation.owningUid, syncOperation.owningPackage, syncOperation.reason, syncOperation.syncSource, info.provider, new Bundle(), syncOperation.allowParallelSyncs)); syncOperation.allowParallelSyncs, syncOperation.isAppStandbyExempted)); } } Loading Loading @@ -3808,6 +3857,10 @@ public class SyncManager { if (key.equals(ContentResolver.SYNC_EXTRAS_INITIALIZE)) { 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; } Loading services/core/java/com/android/server/content/SyncOperation.java +21 −8 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java +28 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.commands.requestsync; import android.accounts.Account; import android.content.ContentResolver; import android.content.SyncRequest; import android.os.Bundle; import java.net.URISyntaxException; Loading @@ -28,12 +29,31 @@ public class RequestSync { private String[] mArgs; private int mNextArg; private String mCurArgData; private boolean mIsForegroundRequest; enum Operation { REQUEST_SYNC { @Override 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 { Loading Loading @@ -191,6 +211,10 @@ public class RequestSync { final String key = nextArgRequired(); final String value = nextArgRequired(); mExtras.putBoolean(key, Boolean.valueOf(value)); } else if (opt.equals("-f") || opt.equals("--foreground")) { mIsForegroundRequest = true; } else { System.err.println("Error: Unknown option: " + opt); showUsage(); Loading Loading @@ -267,6 +291,9 @@ public class RequestSync { " -n|--account-name <ACCOUNT-NAME>\n" + " -t|--account-type <ACCOUNT-TYPE>\n" + " -a|--authority <AUTHORITY>\n" + " App-standby related options\n" + "\n" + " -f|--foreground (Exempt a sync from app standby)\n" + " ContentResolver extra options:\n" + " --is|--ignore-settings: Add SYNC_EXTRAS_IGNORE_SETTINGS\n" + " --ib|--ignore-backoff: Add SYNC_EXTRAS_IGNORE_BACKOFF\n" + Loading
core/java/android/content/ContentResolver.java +21 −7 Original line number Diff line number Diff line Loading @@ -165,6 +165,26 @@ public abstract class ContentResolver { /** {@hide} Flag to allow sync to occur on metered network. */ 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 * the given account/authority pair. One required initialization step is to Loading Loading @@ -2435,13 +2455,7 @@ public abstract class ContentResolver { public static void addPeriodicSync(Account account, String authority, Bundle extras, long pollFrequency) { validateSyncExtrasBundle(extras); if (extras.getBoolean(SYNC_EXTRAS_MANUAL, false) || 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)) { if (invalidPeriodicExtras(extras)) { throw new IllegalArgumentException("illegal extras were set"); } try { Loading
services/core/java/com/android/server/content/ContentService.java +73 −7 Original line number Diff line number Diff line Loading @@ -48,8 +48,8 @@ import android.os.Bundle; import android.os.FactoryTest; import android.os.IBinder; import android.os.Parcel; import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; import android.text.TextUtils; import android.util.ArrayMap; Loading Loading @@ -446,7 +446,7 @@ public final class ContentService extends IContentService.Stub { SyncManager syncManager = getSyncManager(); if (syncManager != null) { syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid, uri.getAuthority()); uri.getAuthority(), /*isAppStandbyExempted=*/ isUidInForeground(uid)); } } Loading Loading @@ -502,6 +502,9 @@ public final class ContentService extends IContentService.Stub { int userId = UserHandle.getCallingUserId(); 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 // process rather than the caller's process. We will restore this before returning. long identityToken = clearCallingIdentity(); Loading @@ -509,7 +512,8 @@ public final class ContentService extends IContentService.Stub { SyncManager syncManager = getSyncManager(); if (syncManager != null) { syncManager.scheduleSync(account, userId, uId, authority, extras, SyncStorageEngine.AuthorityInfo.UNDEFINED); SyncStorageEngine.AuthorityInfo.UNDEFINED, /*isAppStandbyExempted=*/ isForegroundSyncRequest); } } finally { restoreCallingIdentity(identityToken); Loading Loading @@ -548,6 +552,12 @@ public final class ContentService extends IContentService.Stub { public void syncAsUser(SyncRequest request, int userId) { enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId); 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 // process rather than the caller's process. We will restore this before returning. long identityToken = clearCallingIdentity(); Loading @@ -556,8 +566,6 @@ public final class ContentService extends IContentService.Stub { if (syncManager == null) { return; } Bundle extras = request.getBundle(); long flextime = request.getSyncFlexTime(); long runAtTime = request.getSyncRunTime(); if (request.isPeriodic()) { Loading @@ -575,7 +583,8 @@ public final class ContentService extends IContentService.Stub { } else { syncManager.scheduleSync( request.getAccount(), userId, callerUid, request.getProvider(), extras, SyncStorageEngine.AuthorityInfo.UNDEFINED); SyncStorageEngine.AuthorityInfo.UNDEFINED, /*isAppStandbyExempted=*/ isForegroundSyncRequest); } } finally { restoreCallingIdentity(identityToken); Loading Loading @@ -649,10 +658,13 @@ public final class ContentService extends IContentService.Stub { "no permission to write the sync settings"); } Bundle extras = new Bundle(request.getBundle()); validateExtras(callingUid, extras); long identityToken = clearCallingIdentity(); try { SyncStorageEngine.EndPoint info; Bundle extras = new Bundle(request.getBundle()); Account account = request.getAccount(); String provider = request.getProvider(); info = new SyncStorageEngine.EndPoint(account, provider, userId); Loading Loading @@ -787,6 +799,8 @@ public final class ContentService extends IContentService.Stub { mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, "no permission to write the sync settings"); validateExtras(Binder.getCallingUid(), extras); int userId = UserHandle.getCallingUserId(); pollFrequency = clampPeriod(pollFrequency); Loading Loading @@ -815,6 +829,8 @@ public final class ContentService extends IContentService.Stub { mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, "no permission to write the sync settings"); validateExtras(Binder.getCallingUid(), extras); final int callingUid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); Loading Loading @@ -1239,6 +1255,56 @@ public final class ContentService extends IContentService.Stub { 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, * but current unittest framework requires it to be public Loading
services/core/java/com/android/server/content/SyncManager.java +78 −25 Original line number Diff line number Diff line Loading @@ -576,9 +576,10 @@ public class SyncManager { mSyncStorageEngine = SyncStorageEngine.getSingleton(); mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() { @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, AuthorityInfo.UNDEFINED); AuthorityInfo.UNDEFINED, isAppStandbyExempted); } }); Loading Loading @@ -608,7 +609,8 @@ public class SyncManager { if (!removed) { scheduleSync(null, UserHandle.USER_ALL, SyncOperation.REASON_SERVICE_CHANGED, type.authority, null, AuthorityInfo.UNDEFINED); type.authority, null, AuthorityInfo.UNDEFINED, /*isAppStandbyExempted=*/ false); } } }, mSyncHandler); Loading Loading @@ -656,7 +658,8 @@ public class SyncManager { if (mAccountManagerInternal.hasAccountAccess(account, uid)) { scheduleSync(account, UserHandle.getUserId(uid), SyncOperation.REASON_ACCOUNTS_UPDATED, null, null, AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS); null, null, AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS, /*isAppStandbyExempted=*/ false); } }); Loading Loading @@ -881,9 +884,10 @@ public class SyncManager { * Use {@link AuthorityInfo#UNDEFINED} to sync all authorities. */ 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, 0 /* min delay */, true /* checkIfAccountReady */); 0 /* min delay */, true /* checkIfAccountReady */, isAppStandbyExempted); } /** Loading @@ -891,7 +895,8 @@ public class SyncManager { */ private void scheduleSync(Account requestedAccount, int userId, int reason, 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); if (extras == null) { extras = new Bundle(); Loading Loading @@ -1009,7 +1014,8 @@ public class SyncManager { && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) { scheduleSync(account.account, userId, reason, authority, finalExtras, targetSyncState, minDelayMillis, true /* checkIfAccountReady */); true /* checkIfAccountReady */, isAppStandbyExempted); } } )); Loading Loading @@ -1060,7 +1066,7 @@ public class SyncManager { sendOnUnsyncableAccount(mContext, syncAdapterInfo, account.userId, () -> scheduleSync(account.account, account.userId, reason, authority, finalExtras, targetSyncState, minDelayMillis, false)); false, isAppStandbyExempted)); } else { // Initialisation sync. Bundle newExtras = new Bundle(); Loading @@ -1078,7 +1084,8 @@ public class SyncManager { postScheduleSyncMessage( new SyncOperation(account.account, account.userId, owningUid, owningPackage, reason, source, authority, newExtras, allowParallelSyncs), authority, newExtras, allowParallelSyncs, isAppStandbyExempted), minDelayMillis ); } Loading @@ -1095,7 +1102,7 @@ public class SyncManager { postScheduleSyncMessage( new SyncOperation(account.account, account.userId, owningUid, owningPackage, reason, source, authority, extras, allowParallelSyncs), authority, extras, allowParallelSyncs, isAppStandbyExempted), minDelayMillis ); } Loading Loading @@ -1208,11 +1215,13 @@ public class SyncManager { * Schedule sync based on local changes to a provider. We wait for at least LOCAL_SYNC_DELAY * 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(); extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); 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) { Loading Loading @@ -1480,7 +1489,11 @@ public class SyncManager { } // 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) { boolean inheritAppStandbyExemption = false; // Check currently running syncs for (ActiveSyncContext asc: mActiveSyncContexts) { if (asc.mSyncOperation.key.equals(syncOperation.key)) { Loading @@ -1496,14 +1509,14 @@ public class SyncManager { long now = SystemClock.elapsedRealtime(); syncOperation.expectedRuntime = now + minDelay; List<SyncOperation> pending = getAllPendingSyncs(); SyncOperation opWithLeastExpectedRuntime = syncOperation; SyncOperation syncToRun = syncOperation; for (SyncOperation op : pending) { if (op.isPeriodic) { continue; } if (op.key.equals(syncOperation.key)) { if (opWithLeastExpectedRuntime.expectedRuntime > op.expectedRuntime) { opWithLeastExpectedRuntime = op; if (syncToRun.expectedRuntime > op.expectedRuntime) { syncToRun = op; } duplicatesCount++; } Loading @@ -1511,26 +1524,54 @@ public class SyncManager { if (duplicatesCount > 1) { 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) { if (op.isPeriodic) { continue; } if (op.key.equals(syncOperation.key)) { if (op != opWithLeastExpectedRuntime) { if (op != syncToRun) { if (isLoggable) { Slog.v(TAG, "Cancelling duplicate sync " + op); } if (op.isAppStandbyExempted) { inheritAppStandbyExemption = true; } cancelJob(op, "scheduleSyncOperationH-duplicate"); } } } if (opWithLeastExpectedRuntime != syncOperation) { if (syncToRun != syncOperation) { // Don't schedule because a duplicate sync with earlier expected runtime exists. if (isLoggable) { 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; } // 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. Loading @@ -1547,12 +1588,18 @@ public class SyncManager { final int networkType = syncOperation.isNotAllowedOnMetered() ? 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, new ComponentName(mContext, SyncJobService.class)) .setExtras(syncOperation.toJobInfoExtras()) .setRequiredNetworkType(networkType) .setPersisted(true) .setPriority(priority); .setPriority(priority) .setFlags(jobFlags); if (syncOperation.isPeriodic) { b.setPeriodic(syncOperation.periodMillis, syncOperation.flexMillis); Loading Loading @@ -1683,12 +1730,12 @@ public class SyncManager { EndPoint target = new EndPoint(null, null, userId); 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, mContext.getOpPackageName()); for (Account account : accounts) { scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null, AuthorityInfo.NOT_INITIALIZED); AuthorityInfo.NOT_INITIALIZED, /*isAppStandbyExempted=*/ false); } } Loading Loading @@ -3144,7 +3191,8 @@ public class SyncManager { if (syncTargets != null) { scheduleSync(syncTargets.account, syncTargets.userId, SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider, null, AuthorityInfo.NOT_INITIALIZED); null, AuthorityInfo.NOT_INITIALIZED, /*isAppStandbyExempted=*/ false); } } Loading Loading @@ -3211,7 +3259,7 @@ public class SyncManager { syncAdapterInfo.componentName.getPackageName(), SyncOperation.REASON_PERIODIC, SyncStorageEngine.SOURCE_PERIODIC, extras, syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID, pollFrequencyMillis, flexMillis); pollFrequencyMillis, flexMillis, /*isAppStandbyExempted=*/ false); final int syncOpState = computeSyncOpState(op); switch (syncOpState) { Loading Loading @@ -3590,7 +3638,8 @@ public class SyncManager { syncOperation.owningUid, syncOperation.owningPackage, syncOperation.reason, syncOperation.syncSource, info.provider, new Bundle(), syncOperation.allowParallelSyncs)); syncOperation.allowParallelSyncs, syncOperation.isAppStandbyExempted)); } } Loading Loading @@ -3808,6 +3857,10 @@ public class SyncManager { if (key.equals(ContentResolver.SYNC_EXTRAS_INITIALIZE)) { 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; } Loading
services/core/java/com/android/server/content/SyncOperation.java +21 −8 File changed.Preview size limit exceeded, changes collapsed. Show changes