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

Commit a238b468 authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change 4387

* changes:
  beef up the syncadapter API
parents 9d044514 21bb0deb
Loading
Loading
Loading
Loading
+176 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2006 The Android Open Source Project
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -16,39 +16,59 @@

package android.content;

import android.os.*;
import android.os.Process;
import android.accounts.Account;
import android.os.Bundle;
import android.os.Process;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * An abstract implementation of a SyncAdapter that spawns a thread to invoke a sync operation.
 * If a sync operation is already in progress when a startSync() request is received then an error
 * will be returned to the new request and the existing request will be allowed to continue.
 * When a startSync() is received and there is no sync operation in progress then a thread
 * will be started to run the operation and {@link #performSync} will be invoked on that thread.
 * If a cancelSync() is received that matches an existing sync operation then the thread
 * that is running that sync operation will be interrupted, which will indicate to the thread
 * that the sync has been canceled.
 *
 * @hide
 */
public abstract class SyncAdapterNew {
    private static final String TAG = "SyncAdapter";
public abstract class AbstractThreadedSyncAdapter {
    private final Context mContext;
    private final String mAuthority;
    private final AtomicInteger mNumSyncStarts;
    private final ISyncAdapterImpl mISyncAdapterImpl;

    // all accesses to this member variable must be synchronized on "this"
    private SyncThread mSyncThread;

    /** Kernel event log tag.  Also listed in data/etc/event-log-tags. */
    public static final int LOG_SYNC_DETAILS = 2743;

    public SyncAdapterNew(Context context, String authority) {
    /**
     * Creates an {@link AbstractThreadedSyncAdapter}.
     * @param context the {@link Context} that this is running within.
     */
    public AbstractThreadedSyncAdapter(Context context) {
        mContext = context;
        mAuthority = authority;
        mISyncAdapterImpl = new ISyncAdapterImpl();
        mNumSyncStarts = new AtomicInteger(0);
        mSyncThread = null;
    }

    class Transport extends ISyncAdapter.Stub {
        private final AtomicInteger mNumSyncStarts = new AtomicInteger(0);
        private volatile Thread mSyncThread;
    class ISyncAdapterImpl extends ISyncAdapter.Stub {
        public void startSync(ISyncContext syncContext, String authority, Account account,
                Bundle extras) {
            final SyncContext syncContextClient = new SyncContext(syncContext);

        public void startSync(ISyncContext syncContext, Account account, Bundle extras) {
            boolean alreadyInProgress;
            // synchronize to make sure that mSyncThread doesn't change between when we
            // check it and when we use it
            synchronized (this) {
                if (mSyncThread == null) {
                    mSyncThread = new Thread(
                            new SyncRunnable(new SyncContext(syncContext), account, extras),
                            "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet());
                    mSyncThread = new SyncThread(
                            "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(),
                            syncContextClient, authority, account, extras);
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    mSyncThread.start();
                    alreadyInProgress = false;
@@ -57,30 +77,41 @@ public abstract class SyncAdapterNew {
                }
            }

            // do this outside since we don't want to call back into the syncContext while
            // holding the synchronization lock
            if (alreadyInProgress) {
                try {
                    syncContext.onFinished(SyncResult.ALREADY_IN_PROGRESS);
                } catch (RemoteException e) {
                    // don't care if the caller is no longer around
                }
                syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS);
            }
        }

        public void cancelSync() {
        public void cancelSync(ISyncContext syncContext) {
            // synchronize to make sure that mSyncThread doesn't change between when we
            // check it and when we use it
            synchronized (this) {
                if (mSyncThread != null) {
                if (mSyncThread != null
                        && mSyncThread.mSyncContext.getISyncContext() == syncContext) {
                    mSyncThread.interrupt();
                }
            }
        }
    }

        private class SyncRunnable implements Runnable {
    /**
     * The thread that invokes performSync(). It also acquires the provider for this sync
     * before calling performSync and releases it afterwards. Cancel this thread in order to
     * cancel the sync.
     */
    private class SyncThread extends Thread {
        private final SyncContext mSyncContext;
        private final String mAuthority;
        private final Account mAccount;
        private final Bundle mExtras;

            private SyncRunnable(SyncContext syncContext, Account account, Bundle extras) {
        private SyncThread(String name, SyncContext syncContext, String authority,
                Account account, Bundle extras) {
            super(name);
            mSyncContext = syncContext;
            mAuthority = authority;
            mAccount = account;
            mExtras = extras;
        }
@@ -91,11 +122,16 @@ public abstract class SyncAdapterNew {
            }

            SyncResult syncResult = new SyncResult();
                ContentProviderClient provider = mAuthority != null
                        ? mContext.getContentResolver().acquireContentProviderClient(mAuthority)
                        : null;
            ContentProviderClient provider = null;
            try {
                    SyncAdapterNew.this.performSync(mAccount, mExtras, provider, syncResult);
                provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
                if (provider != null) {
                    AbstractThreadedSyncAdapter.this.performSync(mAccount, mExtras,
                            mAuthority, provider, syncResult);
                } else {
                    // TODO(fredq) update the syncResults to indicate that we were unable to
                    // find the provider. maybe with a ProviderError?
                }
            } finally {
                if (provider != null) {
                    provider.release();
@@ -103,23 +139,24 @@ public abstract class SyncAdapterNew {
                if (!isCanceled()) {
                    mSyncContext.onFinished(syncResult);
                }
                // synchronize so that the assignment will be seen by other threads
                // that also synchronize accesses to mSyncThread
                synchronized (this) {
                    mSyncThread = null;
                }
            }
        }

        private boolean isCanceled() {
            return Thread.currentThread().isInterrupted();
        }
    }
    }

    Transport mTransport = new Transport();

    /**
     * Get the Transport object.
     * @return a reference to the ISyncAdapter interface into this SyncAdapter implementation.
     */
    public final ISyncAdapter getISyncAdapter() {
        return mTransport;
        return mISyncAdapterImpl;
    }

    /**
@@ -129,7 +166,11 @@ public abstract class SyncAdapterNew {
     *
     * @param account the account that should be synced
     * @param extras SyncAdapter-specific parameters
     * @param authority the authority of this sync request
     * @param provider a ContentProviderClient that points to the ContentProvider for this
     *   authority
     * @param syncResult SyncAdapter-specific parameters
     */
    public abstract void performSync(Account account, Bundle extras,
            ContentProviderClient provider, SyncResult syncResult);
            String authority, ContentProviderClient provider, SyncResult syncResult);
}
 No newline at end of file
+5 −2
Original line number Diff line number Diff line
@@ -31,14 +31,17 @@ oneway interface ISyncAdapter {
     *
     * @param syncContext the ISyncContext used to indicate the progress of the sync. When
     *   the sync is finished (successfully or not) ISyncContext.onFinished() must be called.
     * @param authority the authority that should be synced
     * @param account the account that should be synced
     * @param extras SyncAdapter-specific parameters
     */
    void startSync(ISyncContext syncContext, in Account account, in Bundle extras);
    void startSync(ISyncContext syncContext, String authority,
      in Account account, in Bundle extras);

    /**
     * Cancel the most recently initiated sync. Due to race conditions, this may arrive
     * after the ISyncContext.onFinished() for that sync was called.
     * @param syncContext the ISyncContext that was passed to {@link #startSync}
     */
    void cancelSync();
    void cancelSync(ISyncContext syncContext);
}
+2 −2
Original line number Diff line number Diff line
@@ -30,12 +30,12 @@ public abstract class SyncAdapter {
    public static final int LOG_SYNC_DETAILS = 2743;

    class Transport extends ISyncAdapter.Stub {
        public void startSync(ISyncContext syncContext, Account account,
        public void startSync(ISyncContext syncContext, String authority, Account account,
                Bundle extras) throws RemoteException {
            SyncAdapter.this.startSync(new SyncContext(syncContext), account, extras);
        }

        public void cancelSync() throws RemoteException {
        public void cancelSync(ISyncContext syncContext) throws RemoteException {
            SyncAdapter.this.cancelSync();
        }
    }
+4 −11
Original line number Diff line number Diff line
@@ -33,8 +33,6 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.RegisteredServicesCache;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
@@ -50,10 +48,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Sync;
import android.provider.Settings;
import android.provider.Sync.History;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.Config;
@@ -77,8 +72,6 @@ import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Random;
import java.util.Observer;
import java.util.Observable;
import java.util.Set;

/**
@@ -1435,7 +1428,7 @@ class SyncManager implements OnAccountsUpdatedListener {
                            // outstanding
                            if (mActiveSyncContext.mSyncAdapter != null) {
                                try {
                                    mActiveSyncContext.mSyncAdapter.cancelSync();
                                    mActiveSyncContext.mSyncAdapter.cancelSync(mActiveSyncContext);
                                } catch (RemoteException e) {
                                    // we don't need to retry this in this case
                                }
@@ -1678,8 +1671,8 @@ class SyncManager implements OnAccountsUpdatedListener {
            mActiveSyncContext.mSyncAdapter = syncAdapter;
            final SyncOperation syncOperation = mActiveSyncContext.mSyncOperation;
            try {
                syncAdapter.startSync(mActiveSyncContext, syncOperation.account,
                        syncOperation.extras);
                syncAdapter.startSync(mActiveSyncContext, syncOperation.authority,
                        syncOperation.account, syncOperation.extras);
            } catch (RemoteException remoteExc) {
                if (Config.LOGD) {
                    Log.d(TAG, "runStateIdle: caught a RemoteException, rescheduling", remoteExc);
@@ -1742,7 +1735,7 @@ class SyncManager implements OnAccountsUpdatedListener {
                }
                if (activeSyncContext.mSyncAdapter != null) {
                    try {
                        activeSyncContext.mSyncAdapter.cancelSync();
                        activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext);
                    } catch (RemoteException e) {
                        // we don't need to retry this in this case
                    }