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

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

Merge "SyncManager: detect suspicious periodic sync removal." into oc-mr1-dev

am: 5d9ec3f8

Change-Id: I7fc888ac5d35a721716f0325793c4c2557a36041
parents 988ef15c 5d9ec3f8
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -646,6 +646,7 @@ public final class ContentService extends IContentService.Stub {
        SyncManager syncManager = getSyncManager();
        if (syncManager == null) return;
        int userId = UserHandle.getCallingUserId();
        final int callingUid = Binder.getCallingUid();

        long identityToken = clearCallingIdentity();
        try {
@@ -658,7 +659,8 @@ public final class ContentService extends IContentService.Stub {
                // Remove periodic sync.
                mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                        "no permission to write the sync settings");
                getSyncManager().removePeriodicSync(info, extras);
                getSyncManager().removePeriodicSync(info, extras,
                        "cancelRequest() by uid=" + callingUid);
            }
            // Cancel active syncs and clear pending syncs from the queue.
            syncManager.cancelScheduledSyncOperation(info, extras);
@@ -814,13 +816,15 @@ 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();

        int userId = UserHandle.getCallingUserId();
        long identityToken = clearCallingIdentity();
        try {
            getSyncManager()
                    .removePeriodicSync(
                            new SyncStorageEngine.EndPoint(account, authority, userId),
                            extras);
                            extras, "removePeriodicSync() by uid=" + callingUid);
        } finally {
            restoreCallingIdentity(identityToken);
        }
+83 −27
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import android.net.NetworkInfo;
import android.net.TrafficStats;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -141,6 +142,8 @@ public class SyncManager {

    private static final boolean DEBUG_ACCOUNT_ACCESS = false;

    private static final boolean ENABLE_SUSPICIOUS_CHECK = Build.IS_DEBUGGABLE;

    /** Delay a sync due to local changes this long. In milliseconds */
    private static final long LOCAL_SYNC_DELAY;

@@ -463,7 +466,7 @@ public class SyncManager {
                        }
                        if (opx.key.equals(opy.key)) {
                            mLogger.log("Removing duplicate sync: ", opy);
                            mJobScheduler.cancel(opy.jobId);
                            cancelJob(opy, "cleanupJobs() x=" + opx + " y=" + opy);
                        }
                    }
                }
@@ -501,15 +504,21 @@ public class SyncManager {
                    }
                }
            }
            if (mLogger.enabled()) {
                mLogger.log("Connected to JobScheduler: "
                        + numPersistedPeriodicSyncs + " periodic syncs "
                        + numPersistedOneshotSyncs + " oneshot syncs.");
            }
            final int totalJobs = (mJobSchedulerInternal == null)
                    ? -1 : mJobSchedulerInternal.countJobs();
            final String summary = "Loaded persisted syncs: "
                    + numPersistedPeriodicSyncs + " periodic syncs, "
                    + numPersistedOneshotSyncs + " oneshot syncs, "
                    + (pendingJobs.size()) + " total system server jobs, "
                    + totalJobs + " total jobs.";
            Slog.i(TAG, summary);
            mLogger.log(summary);

            cleanupJobs();

            if ((numPersistedPeriodicSyncs == 0) && likelyHasPeriodicSyncs()) {
                Slog.wtf(TAG, "Device booted with no persisted periodic syncs.");
            if (ENABLE_SUSPICIOUS_CHECK &&
                    (numPersistedPeriodicSyncs == 0) && likelyHasPeriodicSyncs()) {
                Slog.wtf(TAG, "Device booted with no persisted periodic syncs: " + summary);
            }
        } finally {
            Binder.restoreCallingIdentity(token);
@@ -520,6 +529,7 @@ public class SyncManager {
     * @return whether the device most likely has some periodic syncs.
     */
    private boolean likelyHasPeriodicSyncs() {
        // STOPSHIP Remove the google specific string.
        try {
            return AccountManager.get(mContext).getAccountsByType("com.google").length > 0;
        } catch (Throwable th) {
@@ -566,7 +576,7 @@ public class SyncManager {
        mSyncStorageEngine.setOnAuthorityRemovedListener(new SyncStorageEngine.OnAuthorityRemovedListener() {
            @Override
            public void onAuthorityRemoved(EndPoint removedAuthority) {
                removeSyncsForAuthority(removedAuthority);
                removeSyncsForAuthority(removedAuthority, "onAuthorityRemoved");
            }
        });

@@ -1118,14 +1128,14 @@ public class SyncManager {
        }
    }

    private void removeSyncsForAuthority(EndPoint info) {
    private void removeSyncsForAuthority(EndPoint info, String why) {
        mLogger.log("removeSyncsForAuthority: ", info);
        verifyJobScheduler();
        List<SyncOperation> ops = getAllPendingSyncs();
        for (SyncOperation op: ops) {
            if (op.target.matchesSpec(info)) {
                mLogger.log("canceling: ", op);
                getJobScheduler().cancel(op.jobId);
                cancelJob(op, why);
            }
        }
    }
@@ -1133,8 +1143,9 @@ public class SyncManager {
    /**
     * Remove a specific periodic sync identified by its target and extras.
     */
    public void removePeriodicSync(EndPoint target, Bundle extras) {
        Message m = mSyncHandler.obtainMessage(mSyncHandler.MESSAGE_REMOVE_PERIODIC_SYNC, target);
    public void removePeriodicSync(EndPoint target, Bundle extras, String why) {
        Message m = mSyncHandler.obtainMessage(mSyncHandler.MESSAGE_REMOVE_PERIODIC_SYNC,
                Pair.create(target, why));
        m.setData(extras);
        m.sendToTarget();
    }
@@ -1359,7 +1370,7 @@ public class SyncManager {
        for (SyncOperation op: ops) {
            if (!op.isPeriodic && op.target.matchesSpec(target)) {
                count++;
                getJobScheduler().cancel(op.jobId);
                cancelJob(op, why);
                postScheduleSyncMessage(op, 0 /* min delay */);
            }
        }
@@ -1484,7 +1495,7 @@ public class SyncManager {
                        if (isLoggable) {
                            Slog.v(TAG, "Cancelling duplicate sync " + op);
                        }
                        getJobScheduler().cancel(op.jobId);
                        cancelJob(op, "scheduleSyncOperationH-duplicate");
                    }
                }
            }
@@ -1544,7 +1555,7 @@ public class SyncManager {
        List<SyncOperation> ops = getAllPendingSyncs();
        for (SyncOperation op: ops) {
            if (!op.isPeriodic && op.target.matchesSpec(info)) {
                getJobScheduler().cancel(op.jobId);
                cancelJob(op, "clearScheduledSyncOperations");
                getSyncStorageEngine().markPending(op.target, false);
            }
        }
@@ -1562,7 +1573,7 @@ public class SyncManager {
        for (SyncOperation op: ops) {
            if (!op.isPeriodic && op.target.matchesSpec(info)
                    && syncExtrasEquals(extras, op.extras, false)) {
                getJobScheduler().cancel(op.jobId);
                cancelJob(op, "cancelScheduledSyncOperation");
            }
        }
        setAuthorityPendingState(info);
@@ -1678,7 +1689,7 @@ public class SyncManager {
        List<SyncOperation> ops = getAllPendingSyncs();
        for (SyncOperation op: ops) {
            if (op.target.userId == userId) {
                getJobScheduler().cancel(op.jobId);
                cancelJob(op, "user removed u" + userId);
            }
        }
    }
@@ -2710,7 +2721,8 @@ public class SyncManager {
                                data.flex, data.extras);
                        break;
                    case MESSAGE_REMOVE_PERIODIC_SYNC:
                        removePeriodicSyncH((EndPoint)msg.obj, msg.getData());
                        Pair<EndPoint, String> args = (Pair<EndPoint, String>) (msg.obj);
                        removePeriodicSyncH(args.first, msg.getData(), args.second);
                        break;

                    case SyncHandler.MESSAGE_CANCEL:
@@ -2848,7 +2860,7 @@ public class SyncManager {
                // mSyncJobService.callJobFinished is async, so cancel the job to ensure we don't
                // find the this job in the pending jobs list while looking for duplicates
                // before scheduling it at a later time.
                getJobScheduler().cancel(op.jobId);
                cancelJob(op, "deferSyncH()");
                scheduleSyncOperationH(op, delay);
            }
        }
@@ -2998,7 +3010,7 @@ public class SyncManager {
            for (SyncOperation op: ops) {
                if (!containsAccountAndUser(allAccounts, op.target.account, op.target.userId)) {
                    mLogger.log("canceling: ", op);
                    getJobScheduler().cancel(op.jobId);
                    cancelJob(op, "updateRunningAccountsH()");
                }
            }

@@ -3105,7 +3117,7 @@ public class SyncManager {
        /**
         * Remove this periodic sync operation and all one-off operations initiated by it.
         */
        private void removePeriodicSyncInternalH(SyncOperation syncOperation) {
        private void removePeriodicSyncInternalH(SyncOperation syncOperation, String why) {
            // Remove this periodic sync and all one-off syncs initiated by it.
            List<SyncOperation> ops = getAllPendingSyncs();
            for (SyncOperation op: ops) {
@@ -3117,18 +3129,18 @@ public class SyncManager {
                        runSyncFinishedOrCanceledH(null, asc);
                    }
                    mLogger.log("removePeriodicSyncInternalH-canceling: ", op);
                    getJobScheduler().cancel(op.jobId);
                    cancelJob(op, why);
                }
            }
        }

        private void removePeriodicSyncH(EndPoint target, Bundle extras) {
        private void removePeriodicSyncH(EndPoint target, Bundle extras, String why) {
            verifyJobScheduler();
            List<SyncOperation> ops = getAllPendingSyncs();
            for (SyncOperation op: ops) {
                if (op.isPeriodic && op.target.matchesSpec(target)
                        && syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) {
                    removePeriodicSyncInternalH(op);
                    removePeriodicSyncInternalH(op, why);
                }
            }
        }
@@ -3369,7 +3381,7 @@ public class SyncManager {
                // Note this part is probably okay to do before closeActiveSyncContext()...
                // But moved here to restore OC-dev's behavior.  See b/64597061.
                if (!syncOperation.isPeriodic) {
                    getJobScheduler().cancel(syncOperation.jobId);
                    cancelJob(syncOperation, "runSyncFinishedOrCanceledH()-finished");
                }

                if (!syncResult.hasError()) {
@@ -3410,7 +3422,7 @@ public class SyncManager {
                }

                if (!syncOperation.isPeriodic) {
                    getJobScheduler().cancel(syncOperation.jobId);
                    cancelJob(syncOperation, "runSyncFinishedOrCanceledH()-canceled");
                }

                if (activeSyncContext.mSyncAdapter != null) {
@@ -3747,4 +3759,48 @@ public class SyncManager {
            return mContext;
        }
    }

    private void cancelJob(SyncOperation op, String why) {
        if (op == null) {
            Slog.wtf(TAG, "Null sync operation detected.");
            return;
        }
        if (op.isPeriodic) {
            mLogger.log("Removing periodic sync ", op, " for ", why);

            if (ENABLE_SUSPICIOUS_CHECK && isSuspiciousPeriodicSyncRemoval(op)) {
                wtfWithLog("Suspicious removal of " + op + " for " + why);
            }
        }
        getJobScheduler().cancel(op.jobId);
    }

    private boolean isSuspiciousPeriodicSyncRemoval(SyncOperation op) {
        // STOPSHIP Remove the google specific string.
        if (!op.isPeriodic){
            return false;
        }
        switch (op.target.provider) {
            case "gmail-ls":
            case "com.android.contacts.metadata":
                break;
            default:
                return false;
        }
        final Account account = op.target.account;
        final Account[] accounts = AccountManager.get(mContext)
                .getAccountsByTypeAsUser(account.type, UserHandle.of(op.target.userId));
        for (Account a : accounts) {
            if (a.equals(account)) {
                return true; // Account still exists.  Suspicious!
            }
        }
        // Account no longer exists. Makes sense...
        return false;
    }

    private void wtfWithLog(String message) {
        Slog.wtf(TAG, message);
        mLogger.log("WTF: ", message);
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -37,4 +37,9 @@ public interface JobSchedulerInternal {
    void addBackingUpUid(int uid);
    void removeBackingUpUid(int uid);
    void clearAllBackingUpUids();

    /**
     * @return the total number of jobs across all UIDs.
     */
    int countJobs();
}
+13 −0
Original line number Diff line number Diff line
@@ -1835,6 +1835,13 @@ public final class JobSchedulerService extends com.android.server.SystemService
                }
            }
        }

        @Override
        public int countJobs() {
            synchronized (mLock) {
                return mJobs.size();
            }
        }
    }

    /**
@@ -2015,6 +2022,12 @@ public final class JobSchedulerService extends com.android.server.SystemService
        @Override
        public void cancelAll() throws RemoteException {
            final int uid = Binder.getCallingUid();
            switch (uid) {
                case Process.SYSTEM_UID:
                    // This really shouldn't happen.
                    Slog.wtf(TAG, "JobScheduler.cancelAll() called for uid=" + uid);
                    return;
            }

            long ident = Binder.clearCallingIdentity();
            try {
+5 −8
Original line number Diff line number Diff line
@@ -544,6 +544,7 @@ public final class JobStore {

        @Override
        public void run() {
            int numJobs = 0;
            try {
                List<JobStatus> jobs;
                FileInputStream fis = mJobsFile.openRead();
@@ -557,6 +558,7 @@ public final class JobStore {
                            js.prepareLocked(am);
                            js.enqueueTime = now;
                            this.jobSet.add(js);
                            numJobs++;
                        }
                    }
                }
@@ -565,15 +567,10 @@ public final class JobStore {
                if (DEBUG) {
                    Slog.d(TAG, "Could not find jobs file, probably there was nothing to load.");
                }
            } catch (XmlPullParserException e) {
                if (DEBUG) {
                    Slog.d(TAG, "Error parsing xml.", e);
                }
            } catch (IOException e) {
                if (DEBUG) {
                    Slog.d(TAG, "Error parsing xml.", e);
                }
            } catch (XmlPullParserException | IOException e) {
                Slog.wtf(TAG, "Error jobstore xml.", e);
            }
            Slog.i(TAG, "Read " + numJobs + " jobs");
        }

        private List<JobStatus> readJobMapImpl(FileInputStream fis, boolean rtcIsGood)