- add a "isSyncable" flag to a given account/authority pair that
  indicates whether or not syncs should be attempted for it.
- add public methods to get and set this parameter
diff --git a/api/current.xml b/api/current.xml
index 58c0bf8..8fab662 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -27032,6 +27032,21 @@
 <parameter name="selectionArgs" type="java.lang.String[]">
 </parameter>
 </method>
+<method name="getIsSyncable"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+</method>
 <method name="getMasterSyncAutomatically"
  return="boolean"
  abstract="false"
@@ -27329,6 +27344,23 @@
 <parameter name="extras" type="android.os.Bundle">
 </parameter>
 </method>
+<method name="setIsSyncable"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+<parameter name="syncable" type="int">
+</parameter>
+</method>
 <method name="setMasterSyncAutomatically"
  return="void"
  abstract="false"
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 98ed098..16460db 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1012,6 +1012,31 @@
     }
 
     /**
+     * Check if this account/provider is syncable.
+     * @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet.
+     */
+    public int getIsSyncable(Account account, String authority) {
+        try {
+            return getContentService().getIsSyncable(account, authority);
+        } catch (RemoteException e) {
+            throw new RuntimeException("the ContentService should always be reachable", e);
+        }
+    }
+
+    /**
+     * Set whether this account/provider is syncable.
+     * @param syncable, >0 denotes syncable, 0 means not syncable, <0 means unknown
+     */
+    public void setIsSyncable(Account account, String authority, int syncable) {
+        try {
+            getContentService().setIsSyncable(account, authority, syncable);
+        } catch (RemoteException e) {
+            // exception ignored; if this is thrown then it means the runtime is in the midst of
+            // being restarted
+        }
+    }
+
+    /**
      * Gets the master auto-sync setting that applies to all the providers and accounts.
      * If this is false then the per-provider auto-sync setting is ignored.
      *
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index 7a1ad2b..c4d8aaf 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -272,6 +272,37 @@
         }
     }
 
+    public int getIsSyncable(Account account, String providerName) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
+                "no permission to read the sync settings");
+        long identityToken = clearCallingIdentity();
+        try {
+            SyncManager syncManager = getSyncManager();
+            if (syncManager != null) {
+                return syncManager.getSyncStorageEngine().getIsSyncable(
+                        account, providerName);
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+        return -1;
+    }
+
+    public void setIsSyncable(Account account, String providerName, int syncable) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
+                "no permission to write the sync settings");
+        long identityToken = clearCallingIdentity();
+        try {
+            SyncManager syncManager = getSyncManager();
+            if (syncManager != null) {
+                syncManager.getSyncStorageEngine().setIsSyncable(
+                        account, providerName, syncable);
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
     public boolean getMasterSyncAutomatically() {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                 "no permission to read the sync settings");
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index 658a5bc..b0f14c1 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -54,6 +54,18 @@
      */
     void setSyncAutomatically(in Account account, String providerName, boolean sync);
 
+    /**
+     * Check if this account/provider is syncable.
+     * @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet.
+     */
+    int getIsSyncable(in Account account, String providerName);
+
+    /**
+     * Set whether this account/provider is syncable.
+     * @param syncable, >0 denotes syncable, 0 means not syncable, <0 means unknown
+     */
+    void setIsSyncable(in Account account, String providerName, int syncable);
+
     void setMasterSyncAutomatically(boolean flag);
 
     boolean getMasterSyncAutomatically();
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index f256394..f50fd74 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -589,22 +589,21 @@
         // if the url was specified then replace the list of authorities with just this authority
         // or clear it if this authority isn't syncable
         if (requestedAuthority != null) {
-            final boolean isSyncable = syncableAuthorities.contains(requestedAuthority);
+            final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority);
             syncableAuthorities.clear();
-            if (isSyncable) syncableAuthorities.add(requestedAuthority);
+            if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority);
         }
 
         for (String authority : syncableAuthorities) {
             for (Account account : accounts) {
+                boolean isSyncable = mSyncStorageEngine.getIsSyncable(account, authority) > 0;
+                if (!isSyncable) {
+                    continue;
+                }
                 if (mSyncAdapters.getServiceInfo(new SyncAdapterType(authority, account.type))
                         != null) {
                     scheduleSyncOperation(
                             new SyncOperation(account, source, authority, extras, delay));
-                    // TODO: remove this when Calendar supports multiple accounts. Until then
-                    // pretend that only the first account exists when syncing calendar.
-                    if ("calendar".equals(authority)) {
-                        break;
-                    }
                 }
             }
         }
@@ -1589,9 +1588,11 @@
                     final boolean syncAutomatically =
                             mSyncStorageEngine.getSyncAutomatically(op.account, op.authority)
                                     && mSyncStorageEngine.getMasterSyncAutomatically();
+                    boolean isSyncable =
+                            mSyncStorageEngine.getIsSyncable(op.account, op.authority) > 0;
                     boolean syncAllowed =
                             manualSync || (backgroundDataUsageAllowed && syncAutomatically);
-                    if (!syncAllowed) {
+                    if (!syncAllowed || !isSyncable) {
                         if (isLoggable) {
                             Log.v(TAG, "runStateIdle: sync off, dropping " + op);
                         }
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index b3f9bbb..2647962 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -155,12 +155,15 @@
         final String authority;
         final int ident;
         boolean enabled;
+        int syncable;
 
         AuthorityInfo(Account account, String authority, int ident) {
             this.account = account;
             this.authority = authority;
             this.ident = ident;
             enabled = SYNC_ENABLED_DEFAULT;
+            // TODO: change the default to -1 when the syncadapters are changed to set this
+            syncable = 1;
         }
     }
     
@@ -392,6 +395,44 @@
         reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
     }
 
+    public int getIsSyncable(Account account, String providerName) {
+        synchronized (mAuthorities) {
+            if (account != null) {
+                AuthorityInfo authority = getAuthorityLocked(account, providerName,
+                        "getIsSyncable");
+                if (authority == null) {
+                    return -1;
+                }
+                return authority.syncable;
+            }
+
+            int i = mAuthorities.size();
+            while (i > 0) {
+                i--;
+                AuthorityInfo authority = mAuthorities.get(i);
+                if (authority.authority.equals(providerName)) {
+                    return authority.syncable;
+                }
+            }
+            return -1;
+        }
+    }
+
+    public void setIsSyncable(Account account, String providerName, int syncable) {
+        int oldState;
+        synchronized (mAuthorities) {
+            AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
+            oldState = authority.syncable;
+            authority.syncable = syncable;
+            writeAccountInfoLocked();
+        }
+
+        if (oldState <= 0 && syncable > 0) {
+            mContext.getContentResolver().requestSync(account, providerName, new Bundle());
+        }
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+    }
+
     public void setMasterSyncAutomatically(boolean flag) {
         boolean old;
         synchronized (mAuthorities) {
@@ -1064,10 +1105,12 @@
                                         null, "authority");
                                 String enabled = parser.getAttributeValue(
                                         null, "enabled");
-                                AuthorityInfo authority = mAuthorities.get(id); 
+                                String syncable = parser.getAttributeValue(null, "syncable");
+                                AuthorityInfo authority = mAuthorities.get(id);
                                 if (DEBUG_FILE) Log.v(TAG, "Adding authority: account="
                                         + accountName + " auth=" + authorityName
-                                        + " enabled=" + enabled);
+                                        + " enabled=" + enabled
+                                        + " syncable=" + syncable);
                                 if (authority == null) {
                                     if (DEBUG_FILE) Log.v(TAG, "Creating entry");
                                     authority = getOrCreateAuthorityLocked(
@@ -1077,10 +1120,19 @@
                                 if (authority != null) {
                                     authority.enabled = enabled == null
                                             || Boolean.parseBoolean(enabled);
+                                    if ("unknown".equals(syncable)) {
+                                        authority.syncable = -1;
+                                    } else {
+                                        authority.syncable =
+                                                (syncable == null || Boolean.parseBoolean(enabled))
+                                                        ? 1
+                                                        : 0;
+                                    }
                                 } else {
                                     Log.w(TAG, "Failure adding authority: account="
                                             + accountName + " auth=" + authorityName
-                                            + " enabled=" + enabled);
+                                            + " enabled=" + enabled
+                                            + " syncable=" + syncable);
                                 }
                             }
                         }
@@ -1133,6 +1185,11 @@
                 if (!authority.enabled) {
                     out.attribute(null, "enabled", "false");
                 }
+                if (authority.syncable < 0) {
+                    out.attribute(null, "syncable", "unknown");
+                } else if (authority.syncable == 0) {
+                    out.attribute(null, "syncable", "false");
+                }
                 out.endTag(null, "authority");
             }
             
@@ -1268,6 +1325,7 @@
                         AuthorityInfo authority = mAuthorities.get(i);
                         if (authority.authority.equals(provider)) {
                             authority.enabled = value == null || Boolean.parseBoolean(value);
+                            authority.syncable = 1;
                         }
                     }
                 }