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

Commit b8193487 authored by Makoto Onuki's avatar Makoto Onuki Committed by android-build-merger
Browse files

Merge "AppStandby exemption: sync requested by FG apps" into pi-dev

am: 929ce008

Change-Id: If4d60cefecf3a469ce9a7fbb2690f5120f77b858
parents d1981d6f 929ce008
Loading
Loading
Loading
Loading
+13 −10
Original line number Diff line number Diff line
@@ -29,23 +29,21 @@ public class RequestSync {
    private String[] mArgs;
    private int mNextArg;
    private String mCurArgData;
    private boolean mIsForegroundRequest;

    private int mExemptionFlag = ContentResolver.SYNC_EXEMPTION_NONE;

    enum Operation {
        REQUEST_SYNC {
            @Override
            void invoke(RequestSync caller) {
                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);
                final int flag = caller.mExemptionFlag;
                caller.mExtras.putInt(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG, flag);
                if (flag == ContentResolver.SYNC_EXEMPTION_NONE) {
                    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");
                            + " pass a -f or -F option (use -h for help).\n");
                }
                final SyncRequest request =
                        new SyncRequest.Builder()
@@ -213,7 +211,10 @@ public class RequestSync {
                mExtras.putBoolean(key, Boolean.valueOf(value));

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

            } else if (opt.equals("-F") || opt.equals("--top")) {
                mExemptionFlag = ContentResolver.SYNC_EXEMPTION_ACTIVE_WITH_TEMP;

            } else {
                System.err.println("Error: Unknown option: " + opt);
@@ -293,7 +294,9 @@ public class RequestSync {
                "       -a|--authority <AUTHORITY>\n" +
                "    App-standby related options\n" +
                "\n" +
                "       -f|--foreground (Exempt a sync from app standby)\n" +
                "       -f|--foreground (cause WORKING_SET, FREQUENT sync adapters" +
                        " to run immediately)\n" +
                "       -F|--top (cause even RARE sync adapters to run immediately)\n" +
                "    ContentResolver extra options:\n" +
                "      --is|--ignore-settings: Add SYNC_EXTRAS_IGNORE_SETTINGS\n" +
                "      --ib|--ignore-backoff: Add SYNC_EXTRAS_IGNORE_BACKOFF\n" +
+6 −0
Original line number Diff line number Diff line
@@ -183,10 +183,13 @@ public final class UsageStatsManager {
    public static final int REASON_SUB_USAGE_SLICE_PINNED       = 0x0009;
    /** @hide */
    public static final int REASON_SUB_USAGE_SLICE_PINNED_PRIV  = 0x000A;
    /** @hide */
    public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_START = 0x000B;

    /** @hide */
    public static final int REASON_SUB_PREDICTED_RESTORED       = 0x0001;


    /** @hide */
    @IntDef(flag = false, prefix = { "STANDBY_BUCKET_" }, value = {
            STANDBY_BUCKET_EXEMPTED,
@@ -665,6 +668,9 @@ public final class UsageStatsManager {
                    case REASON_SUB_USAGE_SLICE_PINNED_PRIV:
                        sb.append("slpp");
                        break;
                    case REASON_SUB_USAGE_EXEMPTED_SYNC_START:
                        sb.append("es");
                        break;
                }
                break;
        }
+8 −0
Original line number Diff line number Diff line
@@ -243,4 +243,12 @@ public abstract class UsageStatsManagerInternal {
     */
    public abstract void reportAppJobState(String packageName, @UserIdInt int userId,
            int numDeferredJobs, long timeSinceLastJobRun);

    /**
     * Report a sync that was scheduled by an active app is about to be executed.
     *
     * @param packageName name of the package that owns the sync adapter.
     * @param userId which user the app is associated with
     */
    public abstract void reportExemptedSyncStart(String packageName, @UserIdInt int userId);
}
+34 −13
Original line number Diff line number Diff line
@@ -166,24 +166,13 @@ public abstract class ContentResolver {
    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.
     * {@hide} Integer extra containing a SyncExemption flag.
     *
     * 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";
    public static final String SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG = "v_exemption";

    /**
     * Set by the SyncManager to request that the SyncAdapter initialize itself for
@@ -525,6 +514,38 @@ public abstract class ContentResolver {
     */
    public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 1<<1;

    /**
     * No exception, throttled by app standby normally.
     * @hide
     */
    public static final int SYNC_EXEMPTION_NONE = 0;

    /**
     * When executing a sync with this exemption, we'll put the target app in the ACTIVE bucket
     * for 10 minutes. This will allow the sync adapter to schedule/run further syncs and jobs.
     *
     * Note this will still *not* let RARE apps to run syncs, because they still won't get network
     * connection.
     * @hide
     */
    public static final int SYNC_EXEMPTION_ACTIVE = 1;

    /**
     * In addition to {@link #SYNC_EXEMPTION_ACTIVE}, we put the sync adapter app in the
     * temp whitelist for 10 minutes, so that even RARE apps can run syncs right away.
     * @hide
     */
    public static final int SYNC_EXEMPTION_ACTIVE_WITH_TEMP = 2;

    /** @hide */
    @IntDef(flag = false, prefix = { "SYNC_EXEMPTION_" }, value = {
            SYNC_EXEMPTION_NONE,
            SYNC_EXEMPTION_ACTIVE,
            SYNC_EXEMPTION_ACTIVE_WITH_TEMP,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface SyncExemption {}

    // Always log queries which take 500ms+; shorter queries are
    // sampled accordingly.
    private static final boolean ENABLE_CONTENT_SAMPLE = false;
+39 −38
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.app.job.JobInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentResolver.SyncExemption;
import android.content.Context;
import android.content.IContentService;
import android.content.ISyncStatusObserver;
@@ -78,7 +79,7 @@ import java.util.List;
 */
public final class ContentService extends IContentService.Stub {
    static final String TAG = "ContentService";
    static final boolean DEBUG = false;
    static final boolean DEBUG = true;

    public static class Lifecycle extends SystemService {
        private ContentService mService;
@@ -451,7 +452,7 @@ public final class ContentService extends IContentService.Stub {
                SyncManager syncManager = getSyncManager();
                if (syncManager != null) {
                    syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
                            uri.getAuthority(), /*isAppStandbyExempted=*/ isUidInForeground(uid));
                            uri.getAuthority(), getSyncExemptionForCaller(uid));
                }
            }

@@ -508,7 +509,7 @@ public final class ContentService extends IContentService.Stub {
        int uId = Binder.getCallingUid();

        validateExtras(uId, extras);
        final boolean isForegroundSyncRequest = isForegroundSyncRequest(uId, extras);
        final int syncExemption = getSyncExemptionAndCleanUpExtrasForCaller(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.
@@ -518,7 +519,7 @@ public final class ContentService extends IContentService.Stub {
            if (syncManager != null) {
                syncManager.scheduleSync(account, userId, uId, authority, extras,
                        SyncStorageEngine.AuthorityInfo.UNDEFINED,
                        /*isAppStandbyExempted=*/ isForegroundSyncRequest);
                        syncExemption);
            }
        } finally {
            restoreCallingIdentity(identityToken);
@@ -561,7 +562,7 @@ public final class ContentService extends IContentService.Stub {
        final Bundle extras = request.getBundle();

        validateExtras(callerUid, extras);
        final boolean isForegroundSyncRequest = isForegroundSyncRequest(callerUid, extras);
        final int syncExemption = getSyncExemptionAndCleanUpExtrasForCaller(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.
@@ -589,7 +590,7 @@ public final class ContentService extends IContentService.Stub {
                syncManager.scheduleSync(
                        request.getAccount(), userId, callerUid, request.getProvider(), extras,
                        SyncStorageEngine.AuthorityInfo.UNDEFINED,
                        /*isAppStandbyExempted=*/ isForegroundSyncRequest);
                        syncExemption);
            }
        } finally {
            restoreCallingIdentity(identityToken);
@@ -777,13 +778,15 @@ public final class ContentService extends IContentService.Stub {
                "no permission to write the sync settings");
        enforceCrossUserPermission(userId,
                "no permission to modify the sync settings for user " + userId);
        final int callingUid = Binder.getCallingUid();
        final int syncExemptionFlag = getSyncExemptionForCaller(callingUid);

        long identityToken = clearCallingIdentity();
        try {
            SyncManager syncManager = getSyncManager();
            if (syncManager != null) {
                syncManager.getSyncStorageEngine().setSyncAutomatically(account, userId,
                        providerName, sync);
                        providerName, sync, syncExemptionFlag);
            }
        } finally {
            restoreCallingIdentity(identityToken);
@@ -964,11 +967,14 @@ public final class ContentService extends IContentService.Stub {
        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                "no permission to write the sync settings");

        final int callingUid = Binder.getCallingUid();

        long identityToken = clearCallingIdentity();
        try {
            SyncManager syncManager = getSyncManager();
            if (syncManager != null) {
                syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
                syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId,
                        getSyncExemptionForCaller(callingUid));
            }
        } finally {
            restoreCallingIdentity(identityToken);
@@ -1263,9 +1269,7 @@ public final class ContentService extends IContentService.Stub {
    }

    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)
                ) {
        if (extras.containsKey(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG)) {
            switch (callingUid) {
                case Process.ROOT_UID:
                case Process.SHELL_UID:
@@ -1277,39 +1281,36 @@ public final class ContentService extends IContentService.Stub {
        }
    }

    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);
    @SyncExemption
    private int getSyncExemptionForCaller(int callingUid) {
        return getSyncExemptionAndCleanUpExtrasForCaller(callingUid, null);
    }
        extras.remove(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC);
        extras.remove(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC);

        return isForegroundRequest;
    }
    @SyncExemption
    private int getSyncExemptionAndCleanUpExtrasForCaller(int callingUid, Bundle extras) {
        if (extras != null) {
            final int exemption =
                    extras.getInt(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG, -1);

    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;
            // Need to remove the virtual extra.
            extras.remove(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG);
            if (exemption != -1) {
                return exemption;
            }
        }
        final ActivityManagerInternal ami =
                LocalServices.getService(ActivityManagerInternal.class);
        if (ami != null) {
            return ami.getUidProcessState(uid)
                    <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
        final int procState = (ami != null)
                ? ami.getUidProcessState(callingUid)
                : ActivityManager.PROCESS_STATE_NONEXISTENT;

        if (procState <= ActivityManager.PROCESS_STATE_TOP) {
            return ContentResolver.SYNC_EXEMPTION_ACTIVE_WITH_TEMP;
        }
        return false;
        if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
            return ContentResolver.SYNC_EXEMPTION_ACTIVE;
        }
        return ContentResolver.SYNC_EXEMPTION_NONE;
    }

    /**
Loading