diff options
author | nyquist@chromium.org <nyquist@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-11 19:01:36 +0000 |
---|---|---|
committer | nyquist@chromium.org <nyquist@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-11 19:01:36 +0000 |
commit | 970670d613287319d6c4e9018bd412eb6662f192 (patch) | |
tree | 8643e69723283223bc07f0bda8bb39969ff7f148 /sync/android | |
parent | 6d6fe4afae5ad084c9bd3447e6853a739cbccaa6 (diff) | |
download | chromium_src-970670d613287319d6c4e9018bd412eb6662f192.zip chromium_src-970670d613287319d6c4e9018bd412eb6662f192.tar.gz chromium_src-970670d613287319d6c4e9018bd412eb6662f192.tar.bz2 |
[sync] Add ModelTypeResolver and control types
Adds a few new things:
* Some control ModelTypes (NIGORI, EXPERIMENTS).
* Now we always register control types through setRegisteredTypes()
* Add support for ModelType groups (for AUTOFILL / AUTOFILL_PROFILES)
* Support for refreshing the cacheinvalidation registration.
BUG=113164
Review URL: https://codereview.chromium.org/12217027
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@181730 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sync/android')
6 files changed, 301 insertions, 8 deletions
diff --git a/sync/android/java/src/org/chromium/sync/internal_api/pub/base/ModelType.java b/sync/android/java/src/org/chromium/sync/internal_api/pub/base/ModelType.java index afe6201..4ada2fe 100644 --- a/sync/android/java/src/org/chromium/sync/internal_api/pub/base/ModelType.java +++ b/sync/android/java/src/org/chromium/sync/internal_api/pub/base/ModelType.java @@ -6,11 +6,14 @@ package org.chromium.sync.internal_api.pub.base; import android.util.Log; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.ipc.invalidation.external.client.types.ObjectId; import com.google.protos.ipc.invalidation.Types; import java.util.Collection; +import java.util.HashSet; import java.util.Set; /** @@ -30,17 +33,25 @@ public enum ModelType { */ BOOKMARK("BOOKMARK"), /** + * Flags to enable experimental features. + */ + EXPERIMENTS("EXPERIMENTS", true), + /** + * An object representing a set of Nigori keys. + */ + NIGORI("NIGORI", true), + /** * A password entry. */ PASSWORD("PASSWORD"), /** - * A typed_url folder or a typed_url object. + * An object representing a browser session or tab. */ - TYPED_URL("TYPED_URL"), + SESSION("SESSION"), /** - * An object representing a browser session or tab. + * A typed_url folder or a typed_url object. */ - SESSION("SESSION"); + TYPED_URL("TYPED_URL"); /** Special type representing all possible types. */ public static final String ALL_TYPES_TYPE = "ALL_TYPES"; @@ -49,8 +60,18 @@ public enum ModelType { private final String mModelType; + /** + * True if this is a control type. + */ + private final boolean mControl; + ModelType(String modelType) { + this(modelType, false); + } + + ModelType(String modelType, boolean control) { mModelType = modelType; + mControl = control; } public ObjectId toObjectId() { @@ -83,7 +104,7 @@ public enum ModelType { Set<ModelType> modelTypes = Sets.newHashSetWithExpectedSize(syncTypes.size()); for (String syncType : syncTypes) { try { - modelTypes.add(ModelType.valueOf(syncType)); + modelTypes.add(valueOf(syncType)); } catch (IllegalArgumentException exception) { // Drop invalid sync types. Log.w(TAG, "Could not translate sync type to model type: " + syncType); @@ -110,4 +131,27 @@ public enum ModelType { } return objectIds; } + + /** + * Returns a set of all the control {@link ModelType}s. + */ + public static Set<ModelType> controlTypes() { + Set<ModelType> controlTypes = new HashSet<ModelType>(); + for (ModelType modelType : values()) { + if (modelType.mControl) { + controlTypes.add(modelType); + } + } + return controlTypes; + } + + /** + * Returns a Multimap of all the {@link ModelType} groups. The key is the main + * {@link ModelType}, and the value is a collection of {@link ModelType}s in the same group. + */ + public static Multimap<ModelType, ModelType> modelTypeGroups() { + Multimap<ModelType, ModelType> modelTypeGroups = HashMultimap.create(); + modelTypeGroups.put(AUTOFILL, AUTOFILL_PROFILE); + return modelTypeGroups; + } } diff --git a/sync/android/java/src/org/chromium/sync/notifier/InvalidationController.java b/sync/android/java/src/org/chromium/sync/notifier/InvalidationController.java index 0f4b7d4..9b91c98 100644 --- a/sync/android/java/src/org/chromium/sync/notifier/InvalidationController.java +++ b/sync/android/java/src/org/chromium/sync/notifier/InvalidationController.java @@ -18,6 +18,7 @@ import com.google.common.collect.Lists; import org.chromium.sync.internal_api.pub.base.ModelType; +import java.util.HashSet; import java.util.Set; import javax.annotation.Nullable; @@ -113,12 +114,31 @@ public class InvalidationController { * @param types Set of types for which to register. Ignored if {@code allTypes == true}. */ public void setRegisteredTypes(Account account, boolean allTypes, Set<ModelType> types) { - Intent registerIntent = IntentProtocol.createRegisterIntent(account, allTypes, types); + Set<ModelType> typesToRegister = getModelTypeResolver().resolveModelTypes(types); + Intent registerIntent = IntentProtocol.createRegisterIntent(account, allTypes, + typesToRegister); setDestinationClassName(registerIntent); mContext.startService(registerIntent); } /** + * Reads all stored preferences and calls + * {@link #setRegisteredTypes(android.accounts.Account, boolean, java.util.Set)} with the stored + * values. It can be used on startup of Chrome to ensure we always have a consistent set of + * registrations. + */ + public void refreshRegisteredTypes() { + InvalidationPreferences invalidationPreferences = new InvalidationPreferences(mContext); + Set<String> savedSyncedTypes = invalidationPreferences.getSavedSyncedTypes(); + Account account = invalidationPreferences.getSavedSyncedAccount(); + boolean allTypes = savedSyncedTypes != null && + savedSyncedTypes.contains(ModelType.ALL_TYPES_TYPE); + Set<ModelType> modelTypes = savedSyncedTypes == null ? + new HashSet<ModelType>() : ModelType.syncTypesToModelTypes(savedSyncedTypes); + setRegisteredTypes(account, allTypes, modelTypes); + } + + /** * Starts the invalidation client. */ public void start() { @@ -152,7 +172,8 @@ public class InvalidationController { /** * Creates an instance using {@code context} to send intents. */ - private InvalidationController(Context context) { + @VisibleForTesting + InvalidationController(Context context) { this.mContext = Preconditions.checkNotNull(context.getApplicationContext()); } @@ -192,4 +213,9 @@ public class InvalidationController { } return null; } + + @VisibleForTesting + ModelTypeResolver getModelTypeResolver() { + return new ModelTypeResolverImpl(); + } } diff --git a/sync/android/java/src/org/chromium/sync/notifier/ModelTypeResolver.java b/sync/android/java/src/org/chromium/sync/notifier/ModelTypeResolver.java new file mode 100644 index 0000000..7a30e2f --- /dev/null +++ b/sync/android/java/src/org/chromium/sync/notifier/ModelTypeResolver.java @@ -0,0 +1,17 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.sync.notifier; + +import org.chromium.sync.internal_api.pub.base.ModelType; + +import java.util.Set; + +/** + * A utility class that supports groups of {@link ModelType}s and also supports adding the default + * set of {@link ModelType}s. + */ +interface ModelTypeResolver { + Set<ModelType> resolveModelTypes(Set<ModelType> modelTypes); +} diff --git a/sync/android/java/src/org/chromium/sync/notifier/ModelTypeResolverImpl.java b/sync/android/java/src/org/chromium/sync/notifier/ModelTypeResolverImpl.java new file mode 100644 index 0000000..27a8e6d --- /dev/null +++ b/sync/android/java/src/org/chromium/sync/notifier/ModelTypeResolverImpl.java @@ -0,0 +1,37 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.sync.notifier; + +import com.google.common.collect.Multimap; + +import org.chromium.sync.internal_api.pub.base.ModelType; + +import java.util.HashSet; +import java.util.Set; + +class ModelTypeResolverImpl implements ModelTypeResolver { + @Override + public Set<ModelType> resolveModelTypes(Set<ModelType> modelTypes) { + // Create a new set that we will return as a result, and add all original ModelTypes. + Set<ModelType> typesWithGroups = new HashSet<ModelType>(); + Set<ModelType> modelTypesNonNull = + modelTypes == null ? new HashSet<ModelType>() : modelTypes; + typesWithGroups.addAll(modelTypesNonNull); + + Multimap<ModelType, ModelType> modelTypeGroups = ModelType.modelTypeGroups(); + // Remove ModelTypes that are specified, that does not have their group ModelType specified. + for (ModelType modelType : modelTypeGroups.keySet()) { + if (modelTypesNonNull.contains(modelType)) { + typesWithGroups.addAll(modelTypeGroups.get(modelType)); + } else { + typesWithGroups.removeAll(modelTypeGroups.get(modelType)); + } + } + + // Add all control types. + typesWithGroups.addAll(ModelType.controlTypes()); + return typesWithGroups; + } +} diff --git a/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationControllerTest.java b/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationControllerTest.java index 0a84273..b7f96e3 100644 --- a/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationControllerTest.java +++ b/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationControllerTest.java @@ -19,9 +19,14 @@ import org.chromium.base.test.util.AdvancedMockContext; import org.chromium.base.test.util.Feature; import org.chromium.sync.internal_api.pub.base.ModelType; import org.chromium.sync.notifier.InvalidationController.IntentProtocol; +import org.chromium.sync.signin.AccountManagerHelper; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; /** * Tests for the {@link InvalidationController}. @@ -61,8 +66,22 @@ public class InvalidationControllerTest extends InstrumentationTestCase { @SmallTest @Feature({"Sync"}) public void testRegisterForSpecificTypes() { + final String controllerFlag = "resolveModelTypes"; + final ModelTypeResolver resolver = new ModelTypeResolver() { + @Override + public Set<ModelType> resolveModelTypes(Set<ModelType> modelTypes) { + mContext.setFlag(controllerFlag); + return modelTypes; + } + }; + InvalidationController controller = new InvalidationController(mContext) { + @Override + ModelTypeResolver getModelTypeResolver() { + return resolver; + } + }; Account account = new Account("test@example.com", "bogus"); - mController.setRegisteredTypes(account, false, + controller.setRegisteredTypes(account, false, Sets.newHashSet(ModelType.BOOKMARK, ModelType.SESSION)); assertEquals(1, mContext.getNumStartedIntents()); @@ -81,6 +100,7 @@ public class InvalidationControllerTest extends InstrumentationTestCase { Set<String> actualTypes = Sets.newHashSet(); actualTypes.addAll(intent.getStringArrayListExtra(IntentProtocol.EXTRA_REGISTERED_TYPES)); assertEquals(expectedTypes, actualTypes); + assertTrue(mContext.isFlagSet(controllerFlag)); } @SmallTest @@ -109,6 +129,83 @@ public class InvalidationControllerTest extends InstrumentationTestCase { @SmallTest @Feature({"Sync"}) + public void testRefreshShouldReadValuesFromDiskWithSpecificTypes() { + // Store some preferences for ModelTypes and account. We are using the helper class + // for this, so we don't have to deal with low-level details such as preference keys. + InvalidationPreferences invalidationPreferences = new InvalidationPreferences(mContext); + InvalidationPreferences.EditContext edit = invalidationPreferences.edit(); + Set<String> storedModelTypes = new HashSet<String>(); + storedModelTypes.add(ModelType.BOOKMARK.name()); + storedModelTypes.add(ModelType.TYPED_URL.name()); + invalidationPreferences.setSyncTypes(edit, storedModelTypes); + Account storedAccount = AccountManagerHelper.createAccountFromName("test@gmail.com"); + invalidationPreferences.setAccount(edit, storedAccount); + invalidationPreferences.commit(edit); + + // Ensure all calls to {@link InvalidationController#setRegisteredTypes} store values + // we can inspect in the test. + final AtomicReference<Account> resultAccount = new AtomicReference<Account>(); + final AtomicBoolean resultAllTypes = new AtomicBoolean(); + final AtomicReference<Set<ModelType>> resultTypes = new AtomicReference<Set<ModelType>>(); + InvalidationController controller = new InvalidationController(mContext) { + @Override + public void setRegisteredTypes( + Account account, boolean allTypes, Set<ModelType> types) { + resultAccount.set(account); + resultAllTypes.set(allTypes); + resultTypes.set(types); + } + }; + + // Execute the test. + controller.refreshRegisteredTypes(); + + // Validate the values. + assertEquals(storedAccount, resultAccount.get()); + assertEquals(false, resultAllTypes.get()); + assertEquals(ModelType.syncTypesToModelTypes(storedModelTypes), resultTypes.get()); + } + + @SmallTest + @Feature({"Sync"}) + public void testRefreshShouldReadValuesFromDiskWithAllTypes() { + // Store preferences for the ModelType.ALL_TYPES_TYPE and account. We are using the + // helper class for this, so we don't have to deal with low-level details such as preference + // keys. + InvalidationPreferences invalidationPreferences = new InvalidationPreferences(mContext); + InvalidationPreferences.EditContext edit = invalidationPreferences.edit(); + List<String> storedModelTypes = new ArrayList<String>(); + storedModelTypes.add(ModelType.ALL_TYPES_TYPE); + invalidationPreferences.setSyncTypes(edit, storedModelTypes); + Account storedAccount = AccountManagerHelper.createAccountFromName("test@gmail.com"); + invalidationPreferences.setAccount(edit, storedAccount); + invalidationPreferences.commit(edit); + + // Ensure all calls to {@link InvalidationController#setRegisteredTypes} store values + // we can inspect in the test. + final AtomicReference<Account> resultAccount = new AtomicReference<Account>(); + final AtomicBoolean resultAllTypes = new AtomicBoolean(); + final AtomicReference<Set<ModelType>> resultTypes = new AtomicReference<Set<ModelType>>(); + InvalidationController controller = new InvalidationController(mContext) { + @Override + public void setRegisteredTypes( + Account account, boolean allTypes, Set<ModelType> types) { + resultAccount.set(account); + resultAllTypes.set(allTypes); + resultTypes.set(types); + } + }; + + // Execute the test. + controller.refreshRegisteredTypes(); + + // Validate the values. + assertEquals(storedAccount, resultAccount.get()); + assertEquals(true, resultAllTypes.get()); + } + + @SmallTest + @Feature({"Sync"}) public void testGetContractAuthority() throws Exception { assertEquals(mContext.getPackageName(), mController.getContractAuthority()); } diff --git a/sync/android/javatests/src/org/chromium/sync/notifier/ModelTypeResolverTest.java b/sync/android/javatests/src/org/chromium/sync/notifier/ModelTypeResolverTest.java new file mode 100644 index 0000000..aa53eb8 --- /dev/null +++ b/sync/android/javatests/src/org/chromium/sync/notifier/ModelTypeResolverTest.java @@ -0,0 +1,72 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.sync.notifier; + +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import org.chromium.base.test.util.Feature; +import org.chromium.sync.internal_api.pub.base.ModelType; + +import java.util.HashSet; +import java.util.Set; + +public class ModelTypeResolverTest extends InstrumentationTestCase { + @SmallTest + @Feature({"Sync"}) + public void testControlTypesShouldAlwaysBeAddedEvenForNullModelTypes() throws Exception { + ModelTypeResolverImpl resolver = new ModelTypeResolverImpl(); + Set<ModelType> result = resolver.resolveModelTypes(null); + assertNotNull(result); + assertEquals("Size should be the same as number of control types", + ModelType.controlTypes().size(), result.size()); + assertTrue("Should contain all control ModelTypes", + result.containsAll(ModelType.controlTypes())); + } + + @SmallTest + @Feature({"Sync"}) + public void testControlTypesShouldAlwaysBeAdded() throws Exception { + ModelTypeResolverImpl resolver = new ModelTypeResolverImpl(); + Set<ModelType> result = resolver.resolveModelTypes(new HashSet<ModelType>()); + assertNotNull(result); + assertEquals("Size should be the same as number of control types", + ModelType.controlTypes().size(), result.size()); + assertTrue("Should contain all control ModelTypes", + result.containsAll(ModelType.controlTypes())); + } + + @SmallTest + @Feature({"Sync"}) + public void testAddingAutofillShouldAddAutofillProfile() throws Exception { + Set<ModelType> modelTypes = new HashSet<ModelType>(); + modelTypes.add(ModelType.AUTOFILL); + ModelTypeResolverImpl resolver = new ModelTypeResolverImpl(); + Set<ModelType> result = resolver.resolveModelTypes(modelTypes); + assertNotNull(result); + assertEquals("Size should be 2 plus the number of control types", + 2 + ModelType.controlTypes().size(), result.size()); + assertTrue("Should have AUTOFILL ModelType", result.contains(ModelType.AUTOFILL)); + assertTrue("Should have AUTOFILL_PROFILE ModelType", + result.contains(ModelType.AUTOFILL_PROFILE)); + } + + @SmallTest + @Feature({"Sync"}) + public void testModelTypesThatArePartOfGroupsShouldStillWork() throws Exception { + Set<ModelType> modelTypes = new HashSet<ModelType>(); + modelTypes.add(ModelType.BOOKMARK); + modelTypes.add(ModelType.SESSION); + modelTypes.add(ModelType.TYPED_URL); + ModelTypeResolverImpl resolver = new ModelTypeResolverImpl(); + Set<ModelType> result = resolver.resolveModelTypes(modelTypes); + assertNotNull(result); + assertEquals("Size should be " + modelTypes.size() + " plus the number of control types", + modelTypes.size() + ModelType.controlTypes().size(), result.size()); + assertTrue("Should have BOOKMARK ModelType", result.contains(ModelType.BOOKMARK)); + assertTrue("Should have SESSION ModelType", result.contains(ModelType.SESSION)); + assertTrue("Should have TYPED_URL ModelType", result.contains(ModelType.TYPED_URL)); + } +} |