Exempt sync requests by FG app from app-standby
Test: manual test with request sync, etc Bug: 72443754 Change-Id: Iecf2d3a8c54451324a02ca2762bda72aa219bd92
This commit is contained in:
@@ -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;
|
||||
@@ -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 {
|
||||
@@ -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();
|
||||
@@ -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" +
|
||||
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
@@ -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()) {
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -881,17 +884,19 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param minDelayMillis The sync can't land before this delay expires.
|
||||
*/
|
||||
private void scheduleSync(Account requestedAccount, int userId, int reason,
|
||||
String requestedAuthority, Bundle extras, int targetSyncState,
|
||||
final long minDelayMillis, boolean checkIfAccountReady) {
|
||||
String requestedAuthority, Bundle extras, int targetSyncState,
|
||||
final long minDelayMillis, boolean checkIfAccountReady,
|
||||
boolean isAppStandbyExempted) {
|
||||
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
|
||||
if (extras == null) {
|
||||
extras = new Bundle();
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
));
|
||||
@@ -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();
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
@@ -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) {
|
||||
@@ -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)) {
|
||||
@@ -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++;
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,29 +98,33 @@ public class SyncOperation {
|
||||
/** jobId of the JobScheduler job corresponding to this sync */
|
||||
public int jobId;
|
||||
|
||||
/** Whether this operation should be exempted from the app-standby throttling. */
|
||||
public boolean isAppStandbyExempted;
|
||||
|
||||
public SyncOperation(Account account, int userId, int owningUid, String owningPackage,
|
||||
int reason, int source, String provider, Bundle extras,
|
||||
boolean allowParallelSyncs) {
|
||||
boolean allowParallelSyncs, boolean isAppStandbyExempted) {
|
||||
this(new SyncStorageEngine.EndPoint(account, provider, userId), owningUid, owningPackage,
|
||||
reason, source, extras, allowParallelSyncs);
|
||||
reason, source, extras, allowParallelSyncs, isAppStandbyExempted);
|
||||
}
|
||||
|
||||
private SyncOperation(SyncStorageEngine.EndPoint info, int owningUid, String owningPackage,
|
||||
int reason, int source, Bundle extras, boolean allowParallelSyncs) {
|
||||
int reason, int source, Bundle extras, boolean allowParallelSyncs,
|
||||
boolean isAppStandbyExempted) {
|
||||
this(info, owningUid, owningPackage, reason, source, extras, allowParallelSyncs, false,
|
||||
NO_JOB_ID, 0, 0);
|
||||
NO_JOB_ID, 0, 0, isAppStandbyExempted);
|
||||
}
|
||||
|
||||
public SyncOperation(SyncOperation op, long periodMillis, long flexMillis) {
|
||||
this(op.target, op.owningUid, op.owningPackage, op.reason, op.syncSource,
|
||||
new Bundle(op.extras), op.allowParallelSyncs, op.isPeriodic, op.sourcePeriodicId,
|
||||
periodMillis, flexMillis);
|
||||
periodMillis, flexMillis, /*isAppStandbyExempted=*/ false);
|
||||
}
|
||||
|
||||
public SyncOperation(SyncStorageEngine.EndPoint info, int owningUid, String owningPackage,
|
||||
int reason, int source, Bundle extras, boolean allowParallelSyncs,
|
||||
boolean isPeriodic, int sourcePeriodicId, long periodMillis,
|
||||
long flexMillis) {
|
||||
long flexMillis, boolean isAppStandbyExempted) {
|
||||
this.target = info;
|
||||
this.owningUid = owningUid;
|
||||
this.owningPackage = owningPackage;
|
||||
@@ -134,6 +138,7 @@ public class SyncOperation {
|
||||
this.flexMillis = flexMillis;
|
||||
this.jobId = NO_JOB_ID;
|
||||
this.key = toKey();
|
||||
this.isAppStandbyExempted = isAppStandbyExempted;
|
||||
}
|
||||
|
||||
/* Get a one off sync operation instance from a periodic sync. */
|
||||
@@ -143,7 +148,7 @@ public class SyncOperation {
|
||||
}
|
||||
SyncOperation op = new SyncOperation(target, owningUid, owningPackage, reason, syncSource,
|
||||
new Bundle(extras), allowParallelSyncs, false, jobId /* sourcePeriodicId */,
|
||||
periodMillis, flexMillis);
|
||||
periodMillis, flexMillis, /*isAppStandbyExempted=*/ false);
|
||||
return op;
|
||||
}
|
||||
|
||||
@@ -161,6 +166,7 @@ public class SyncOperation {
|
||||
periodMillis = other.periodMillis;
|
||||
flexMillis = other.flexMillis;
|
||||
this.key = other.key;
|
||||
isAppStandbyExempted = other.isAppStandbyExempted;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,6 +235,7 @@ public class SyncOperation {
|
||||
jobInfoExtras.putLong("flexMillis", flexMillis);
|
||||
jobInfoExtras.putLong("expectedRuntime", expectedRuntime);
|
||||
jobInfoExtras.putInt("retries", retries);
|
||||
jobInfoExtras.putBoolean("isAppStandbyExempted", isAppStandbyExempted);
|
||||
return jobInfoExtras;
|
||||
}
|
||||
|
||||
@@ -249,6 +256,7 @@ public class SyncOperation {
|
||||
Bundle extras;
|
||||
boolean allowParallelSyncs, isPeriodic;
|
||||
long periodMillis, flexMillis;
|
||||
boolean isAppStandbyExempted;
|
||||
|
||||
if (!jobExtras.getBoolean("SyncManagerJob", false)) {
|
||||
return null;
|
||||
@@ -267,6 +275,7 @@ public class SyncOperation {
|
||||
initiatedBy = jobExtras.getInt("sourcePeriodicId", NO_JOB_ID);
|
||||
periodMillis = jobExtras.getLong("periodMillis");
|
||||
flexMillis = jobExtras.getLong("flexMillis");
|
||||
isAppStandbyExempted = jobExtras.getBoolean("isAppStandbyExempted", false);
|
||||
extras = new Bundle();
|
||||
|
||||
PersistableBundle syncExtras = jobExtras.getPersistableBundle("syncExtras");
|
||||
@@ -288,7 +297,8 @@ public class SyncOperation {
|
||||
SyncStorageEngine.EndPoint target =
|
||||
new SyncStorageEngine.EndPoint(account, provider, userId);
|
||||
SyncOperation op = new SyncOperation(target, owningUid, owningPackage, reason, source,
|
||||
extras, allowParallelSyncs, isPeriodic, initiatedBy, periodMillis, flexMillis);
|
||||
extras, allowParallelSyncs, isPeriodic, initiatedBy, periodMillis, flexMillis,
|
||||
isAppStandbyExempted);
|
||||
op.jobId = jobExtras.getInt("jobId");
|
||||
op.expectedRuntime = jobExtras.getLong("expectedRuntime");
|
||||
op.retries = jobExtras.getInt("retries");
|
||||
@@ -375,6 +385,9 @@ public class SyncOperation {
|
||||
if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
|
||||
sb.append(" EXPEDITED");
|
||||
}
|
||||
if (isAppStandbyExempted) {
|
||||
sb.append(" STANDBY-EXEMPTED");
|
||||
}
|
||||
sb.append(" Reason=");
|
||||
sb.append(reasonToString(pm, reason));
|
||||
if (isPeriodic) {
|
||||
|
||||
@@ -340,7 +340,8 @@ public class SyncStorageEngine {
|
||||
interface OnSyncRequestListener {
|
||||
|
||||
/** Called when a sync is needed on an account(s) due to some change in state. */
|
||||
public void onSyncRequest(EndPoint info, int reason, Bundle extras);
|
||||
public void onSyncRequest(EndPoint info, int reason, Bundle extras,
|
||||
boolean exemptFromAppStandby);
|
||||
}
|
||||
|
||||
interface PeriodicSyncAddedListener {
|
||||
@@ -675,7 +676,8 @@ public class SyncStorageEngine {
|
||||
|
||||
if (sync) {
|
||||
requestSync(account, userId, SyncOperation.REASON_SYNC_AUTO, providerName,
|
||||
new Bundle());
|
||||
new Bundle(),
|
||||
/* exemptFromAppStandby=*/ false);
|
||||
}
|
||||
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
|
||||
queueBackup();
|
||||
@@ -736,7 +738,8 @@ public class SyncStorageEngine {
|
||||
writeAccountInfoLocked();
|
||||
}
|
||||
if (syncable == AuthorityInfo.SYNCABLE) {
|
||||
requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle());
|
||||
requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle(),
|
||||
/*exemptFromAppStandby=*/ false); // Or the caller FG state?
|
||||
}
|
||||
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
|
||||
}
|
||||
@@ -908,7 +911,8 @@ public class SyncStorageEngine {
|
||||
}
|
||||
if (flag) {
|
||||
requestSync(null, userId, SyncOperation.REASON_MASTER_SYNC_AUTO, null,
|
||||
new Bundle());
|
||||
new Bundle(),
|
||||
/*exemptFromAppStandby=*/ false); // Or the caller FG state?
|
||||
}
|
||||
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
|
||||
mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED);
|
||||
@@ -2138,10 +2142,12 @@ public class SyncStorageEngine {
|
||||
}
|
||||
}
|
||||
|
||||
private void requestSync(AuthorityInfo authorityInfo, int reason, Bundle extras) {
|
||||
private void requestSync(AuthorityInfo authorityInfo, int reason, Bundle extras,
|
||||
boolean exemptFromAppStandby) {
|
||||
if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID
|
||||
&& mSyncRequestListener != null) {
|
||||
mSyncRequestListener.onSyncRequest(authorityInfo.target, reason, extras);
|
||||
mSyncRequestListener.onSyncRequest(authorityInfo.target, reason, extras,
|
||||
exemptFromAppStandby);
|
||||
} else {
|
||||
SyncRequest.Builder req =
|
||||
new SyncRequest.Builder()
|
||||
@@ -2153,7 +2159,7 @@ public class SyncStorageEngine {
|
||||
}
|
||||
|
||||
private void requestSync(Account account, int userId, int reason, String authority,
|
||||
Bundle extras) {
|
||||
Bundle extras, boolean exemptFromAppStandby) {
|
||||
// If this is happening in the system process, then call the syncrequest listener
|
||||
// to make a request back to the SyncManager directly.
|
||||
// If this is probably a test instance, then call back through the ContentResolver
|
||||
@@ -2162,8 +2168,7 @@ public class SyncStorageEngine {
|
||||
&& mSyncRequestListener != null) {
|
||||
mSyncRequestListener.onSyncRequest(
|
||||
new EndPoint(account, authority, userId),
|
||||
reason,
|
||||
extras);
|
||||
reason, extras, exemptFromAppStandby);
|
||||
} else {
|
||||
ContentResolver.requestSync(account, authority, extras);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import android.test.suitebuilder.annotation.SmallTest;
|
||||
/**
|
||||
* Test for SyncOperation.
|
||||
*
|
||||
* bit FrameworksServicesTests:com.android.server.content.SyncOperationTest
|
||||
* atest ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
|
||||
*/
|
||||
@SmallTest
|
||||
public class SyncOperationTest extends AndroidTestCase {
|
||||
@@ -59,7 +59,8 @@ public class SyncOperationTest extends AndroidTestCase {
|
||||
SyncOperation.REASON_PERIODIC,
|
||||
"authority1",
|
||||
b1,
|
||||
false);
|
||||
false,
|
||||
/*isAppStandbyExempted=*/ false);
|
||||
|
||||
// Same as op1 but different time infos
|
||||
SyncOperation op2 = new SyncOperation(account1, 0,
|
||||
@@ -67,7 +68,8 @@ public class SyncOperationTest extends AndroidTestCase {
|
||||
SyncOperation.REASON_PERIODIC,
|
||||
"authority1",
|
||||
b1,
|
||||
false);
|
||||
false,
|
||||
/*isAppStandbyExempted=*/ false);
|
||||
|
||||
// Same as op1 but different authority
|
||||
SyncOperation op3 = new SyncOperation(account1, 0,
|
||||
@@ -75,7 +77,8 @@ public class SyncOperationTest extends AndroidTestCase {
|
||||
SyncOperation.REASON_PERIODIC,
|
||||
"authority2",
|
||||
b1,
|
||||
false);
|
||||
false,
|
||||
/*isAppStandbyExempted=*/ false);
|
||||
|
||||
// Same as op1 but different account
|
||||
SyncOperation op4 = new SyncOperation(account2, 0,
|
||||
@@ -83,7 +86,8 @@ public class SyncOperationTest extends AndroidTestCase {
|
||||
SyncOperation.REASON_PERIODIC,
|
||||
"authority1",
|
||||
b1,
|
||||
false);
|
||||
false,
|
||||
/*isAppStandbyExempted=*/ false);
|
||||
|
||||
// Same as op1 but different bundle
|
||||
SyncOperation op5 = new SyncOperation(account1, 0,
|
||||
@@ -91,7 +95,8 @@ public class SyncOperationTest extends AndroidTestCase {
|
||||
SyncOperation.REASON_PERIODIC,
|
||||
"authority1",
|
||||
b2,
|
||||
false);
|
||||
false,
|
||||
/*isAppStandbyExempted=*/ false);
|
||||
|
||||
assertEquals(op1.key, op2.key);
|
||||
assertNotSame(op1.key, op3.key);
|
||||
@@ -111,7 +116,8 @@ public class SyncOperationTest extends AndroidTestCase {
|
||||
SyncOperation.REASON_PERIODIC,
|
||||
"authority1",
|
||||
b1,
|
||||
false);
|
||||
false,
|
||||
/*isAppStandbyExempted=*/ false);
|
||||
|
||||
PersistableBundle pb = op1.toJobInfoExtras();
|
||||
SyncOperation op2 = SyncOperation.maybeCreateFromJobExtras(pb);
|
||||
@@ -138,7 +144,8 @@ public class SyncOperationTest extends AndroidTestCase {
|
||||
"provider", 0);
|
||||
Bundle extras = new Bundle();
|
||||
SyncOperation periodic = new SyncOperation(ep, 0, "package", 0, 0, extras, false, true,
|
||||
SyncOperation.NO_JOB_ID, 60000, 10000);
|
||||
SyncOperation.NO_JOB_ID, 60000, 10000,
|
||||
/*isAppStandbyExempted=*/ false);
|
||||
SyncOperation oneoff = periodic.createOneTimeSyncOperation();
|
||||
assertFalse("Conversion to oneoff sync failed.", oneoff.isPeriodic);
|
||||
assertEquals("Period not restored", periodic.periodMillis, oneoff.periodMillis);
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.test.mock.MockContext;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.test.suitebuilder.annotation.Suppress;
|
||||
|
||||
import com.android.internal.os.AtomicFile;
|
||||
|
||||
@@ -44,6 +45,7 @@ import java.io.FileOutputStream;
|
||||
*
|
||||
* TODO Broken. Fix it. b/62485315
|
||||
*/
|
||||
@Suppress
|
||||
public class SyncStorageEngineTest extends AndroidTestCase {
|
||||
|
||||
protected Account account1;
|
||||
@@ -101,7 +103,8 @@ public class SyncStorageEngineTest extends AndroidTestCase {
|
||||
SyncOperation.REASON_PERIODIC,
|
||||
SyncStorageEngine.SOURCE_LOCAL,
|
||||
authority,
|
||||
Bundle.EMPTY, true);
|
||||
Bundle.EMPTY, true,
|
||||
/*isAppStandbyExempted=*/ false);
|
||||
long historyId = engine.insertStartSyncEvent(op, time0);
|
||||
long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2;
|
||||
engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0);
|
||||
|
||||
Reference in New Issue
Block a user