summaryrefslogtreecommitdiffstats
path: root/sync/android
diff options
context:
space:
mode:
Diffstat (limited to 'sync/android')
-rw-r--r--sync/android/java/src/org/chromium/sync/notifier/InvalidationClientNameGenerator.java10
-rw-r--r--sync/android/java/src/org/chromium/sync/notifier/InvalidationClientNameProvider.java51
-rw-r--r--sync/android/java/src/org/chromium/sync/notifier/InvalidationIntentProtocol.java5
-rw-r--r--sync/android/java/src/org/chromium/sync/notifier/InvalidationService.java19
-rw-r--r--sync/android/java/src/org/chromium/sync/notifier/RandomizedInvalidationClientNameGenerator.java41
-rw-r--r--sync/android/javatests/src/org/chromium/sync/notifier/InvalidationClientNameProviderTest.java67
-rw-r--r--sync/android/javatests/src/org/chromium/sync/notifier/InvalidationServiceTest.java7
7 files changed, 171 insertions, 29 deletions
diff --git a/sync/android/java/src/org/chromium/sync/notifier/InvalidationClientNameGenerator.java b/sync/android/java/src/org/chromium/sync/notifier/InvalidationClientNameGenerator.java
new file mode 100644
index 0000000..dcc95e4
--- /dev/null
+++ b/sync/android/java/src/org/chromium/sync/notifier/InvalidationClientNameGenerator.java
@@ -0,0 +1,10 @@
+// Copyright 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;
+
+/** Interface for classes that create an Invalidation client's name. */
+public interface InvalidationClientNameGenerator {
+ public byte[] generateInvalidatorClientName();
+}
diff --git a/sync/android/java/src/org/chromium/sync/notifier/InvalidationClientNameProvider.java b/sync/android/java/src/org/chromium/sync/notifier/InvalidationClientNameProvider.java
new file mode 100644
index 0000000..08e0062
--- /dev/null
+++ b/sync/android/java/src/org/chromium/sync/notifier/InvalidationClientNameProvider.java
@@ -0,0 +1,51 @@
+// Copyright 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;
+
+/**
+ * An injectable singleton that provides an invalidation client with an appropriate unique name.
+ *
+ * This singleton will always provide a somewhat reasonable name. With proper support from outside
+ * components, it will be able to provide a name that is consistent across restarts.
+ */
+public class InvalidationClientNameProvider {
+ private static final Object LOCK = new Object();
+
+ private static InvalidationClientNameProvider sInstance;
+
+ private final Object mLock;
+
+ private InvalidationClientNameGenerator mGenerator;
+
+ private byte[] mUniqueId;
+
+ public static InvalidationClientNameProvider get() {
+ synchronized (LOCK) {
+ if (sInstance == null) {
+ sInstance = new InvalidationClientNameProvider();
+ }
+ return sInstance;
+ }
+ }
+
+ InvalidationClientNameProvider() {
+ mLock = new Object();
+ mGenerator = new RandomizedInvalidationClientNameGenerator();
+ }
+
+ /** Returns a consistent unique string of bytes for use as an invalidator client ID. */
+ public byte[] getInvalidatorClientName() {
+ synchronized (mLock) {
+ if (mUniqueId == null) {
+ mUniqueId = mGenerator.generateInvalidatorClientName();
+ }
+ return mUniqueId;
+ }
+ }
+
+ public void setPreferredClientNameGenerator(InvalidationClientNameGenerator generator) {
+ mGenerator = generator;
+ }
+}
diff --git a/sync/android/java/src/org/chromium/sync/notifier/InvalidationIntentProtocol.java b/sync/android/java/src/org/chromium/sync/notifier/InvalidationIntentProtocol.java
index e3d5135..c87b894 100644
--- a/sync/android/java/src/org/chromium/sync/notifier/InvalidationIntentProtocol.java
+++ b/sync/android/java/src/org/chromium/sync/notifier/InvalidationIntentProtocol.java
@@ -35,11 +35,6 @@ public class InvalidationIntentProtocol {
public static final String EXTRA_ACCOUNT = "account";
/**
- * byte[]-valued intent extra containing the unique client ID.
- */
- public static final String EXTRA_CLIENT_NAME = "client_name";
-
- /**
* String-list-valued intent extra of the syncable types to sync.
*/
public static final String EXTRA_REGISTERED_TYPES = "registered_types";
diff --git a/sync/android/java/src/org/chromium/sync/notifier/InvalidationService.java b/sync/android/java/src/org/chromium/sync/notifier/InvalidationService.java
index cd8b605..24a77cd 100644
--- a/sync/android/java/src/org/chromium/sync/notifier/InvalidationService.java
+++ b/sync/android/java/src/org/chromium/sync/notifier/InvalidationService.java
@@ -75,11 +75,6 @@ public class InvalidationService extends AndroidListener {
*/
@Nullable private static byte[] sClientId;
- /**
- * A ID that uniquely identifies this client. Used for reflection blocking.
- */
- @Nullable private byte[] mClientName;
-
@Override
public void onHandleIntent(Intent intent) {
// Ensure that a client is or is not running, as appropriate, and that it is for the
@@ -91,16 +86,6 @@ public class InvalidationService extends AndroidListener {
(Account) intent.getParcelableExtra(InvalidationIntentProtocol.EXTRA_ACCOUNT)
: null;
- // Any intents sent to the InvalidationService should include the EXTRA_CLIENT_NAME. The
- // call to ensureClientStartState() might need a client name, and would break if we don't
- // have one.
- //
- // Intents that are addressed to the AndroidListener portion of this class do not need to
- // include the EXTRA_CLIENT_NAME.
- if (intent.hasExtra(InvalidationIntentProtocol.EXTRA_CLIENT_NAME)) {
- mClientName = intent.getByteArrayExtra(InvalidationIntentProtocol.EXTRA_CLIENT_NAME);
- }
-
ensureAccount(account);
ensureClientStartState();
@@ -281,8 +266,8 @@ public class InvalidationService extends AndroidListener {
* {@link InvalidationPreferences#setAccount}.
*/
private void startClient() {
- assert (mClientName != null);
- Intent startIntent = AndroidListener.createStartIntent(this, CLIENT_TYPE, mClientName);
+ byte[] clientName = InvalidationClientNameProvider.get().getInvalidatorClientName();
+ Intent startIntent = AndroidListener.createStartIntent(this, CLIENT_TYPE, clientName);
startService(startIntent);
setIsClientStarted(true);
}
diff --git a/sync/android/java/src/org/chromium/sync/notifier/RandomizedInvalidationClientNameGenerator.java b/sync/android/java/src/org/chromium/sync/notifier/RandomizedInvalidationClientNameGenerator.java
new file mode 100644
index 0000000..6374f51
--- /dev/null
+++ b/sync/android/java/src/org/chromium/sync/notifier/RandomizedInvalidationClientNameGenerator.java
@@ -0,0 +1,41 @@
+// Copyright 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.util.Base64;
+
+import org.chromium.sync.notifier.InvalidationClientNameGenerator;
+
+import java.util.Random;
+
+/**
+ * Generates a fully random client ID.
+ *
+ * This ID will not persist across restarts. Using this ID will break the invalidator's "reflection
+ * blocking" feature. That's unfortunate, but better than using a hard-coded ID. A hard-coded ID
+ * could prevent invalidations from being delivered.
+ */
+class RandomizedInvalidationClientNameGenerator implements InvalidationClientNameGenerator {
+ private static final Random RANDOM = new Random();
+
+ RandomizedInvalidationClientNameGenerator() {}
+
+ /**
+ * Generates a random ID prefixed with the string "BadID".
+ *
+ * The prefix is intended to grab attention. We should never use it in real builds. Hopefully,
+ * it will induce someone to file a bug if they see it.
+ *
+ * However, as bad as it is, this ID is better than a hard-coded default or none at all. See
+ * the class description for more details.
+ */
+ public byte[] generateInvalidatorClientName() {
+ byte[] randomBytes = new byte[8];
+ RANDOM.nextBytes(randomBytes);
+ String encoded = Base64.encodeToString(randomBytes, 0, randomBytes.length, Base64.NO_WRAP);
+ String idString = "BadID" + encoded;
+ return idString.getBytes();
+ }
+}
diff --git a/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationClientNameProviderTest.java b/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationClientNameProviderTest.java
new file mode 100644
index 0000000..d24aa89
--- /dev/null
+++ b/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationClientNameProviderTest.java
@@ -0,0 +1,67 @@
+// Copyright 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.notifier.InvalidationClientNameProvider;
+
+import java.util.Arrays;
+
+/** Tests for the {@link InvalidationClientNameProvider} */
+public class InvalidationClientNameProviderTest extends InstrumentationTestCase {
+ private InvalidationClientNameProvider mProvider;
+
+ @Override
+ protected void setUp() {
+ mProvider = new InvalidationClientNameProvider();
+ }
+
+ @SmallTest
+ @Feature({"Sync"})
+ public void testFallbackClientId() {
+ // Test that the InvalidationController consistently returns the same ID even when it has to
+ // resort to its "fallback" ID generation code.
+ byte[] id1 = mProvider.getInvalidatorClientName();
+ byte[] id2 = mProvider.getInvalidatorClientName();
+
+ // We expect the returned IDs to be consistent in every call.
+ assertTrue("Expected returned IDs to be consistent", Arrays.equals(id1, id2));
+
+ // Even if initialize the generator late, the ID will remain consistent.
+ registerHardCodedGenerator(mProvider);
+
+ // IDs should still be consistent, even if we change the generator.
+ // (In the real program, the generator should be set before anyone invokes the
+ // getInvalidatorClientName() and never change afterwards. We test this anyway to make sure
+ // nothing will blow up if someone accidentally violates that constraint.)
+ byte[] id3 = mProvider.getInvalidatorClientName();
+ assertTrue("Changing generators should not affect returned ID consistency",
+ Arrays.equals(id2, id3));
+ }
+
+ @SmallTest
+ @Feature({"Sync"})
+ public void testPreRegisteredGenerator() {
+ registerHardCodedGenerator(mProvider);
+
+ byte[] id = mProvider.getInvalidatorClientName();
+ byte[] id2 = mProvider.getInvalidatorClientName();
+
+ // Expect that consistent IDs are maintained when using a custom generator, too.
+ assertTrue("Custom generators should return consistent IDs", Arrays.equals(id, id2));
+ }
+
+ private static void registerHardCodedGenerator(InvalidationClientNameProvider provider) {
+ provider.setPreferredClientNameGenerator(
+ new InvalidationClientNameGenerator() {
+ public byte[] generateInvalidatorClientName() {
+ return "Testable ID".getBytes();
+ }
+ });
+ }
+}
diff --git a/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationServiceTest.java b/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationServiceTest.java
index b4b536f..fcdc65e 100644
--- a/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationServiceTest.java
+++ b/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationServiceTest.java
@@ -41,9 +41,6 @@ public class InvalidationServiceTest extends ServiceTestCase<TestableInvalidatio
/** Id used when creating clients. */
private static final byte[] CLIENT_ID = new byte[]{0, 4, 7};
- /** Id used to uniquely name this client instance. */
- private static final byte[] TEST_CLIENT_NAME = "UNIQUE_CLIENT_NAME".getBytes();
-
/** Intents provided to {@link #startService}. */
private List<Intent> mStartServiceIntents;
@@ -817,7 +814,6 @@ public class InvalidationServiceTest extends ServiceTestCase<TestableInvalidatio
/** Creates an intent to start the InvalidationService. */
private Intent createStartIntent() {
Intent intent = new Intent();
- intent.putExtra(InvalidationIntentProtocol.EXTRA_CLIENT_NAME, TEST_CLIENT_NAME);
return intent;
}
@@ -825,14 +821,12 @@ public class InvalidationServiceTest extends ServiceTestCase<TestableInvalidatio
private Intent createStopIntent() {
Intent intent = new Intent();
intent.putExtra(InvalidationIntentProtocol.EXTRA_STOP, true);
- intent.putExtra(InvalidationIntentProtocol.EXTRA_CLIENT_NAME, TEST_CLIENT_NAME);
return intent;
}
/** Creates an intent to register some types with the InvalidationService. */
private Intent createRegisterIntent(Account account, boolean allTypes, Set<ModelType> types) {
Intent intent = InvalidationIntentProtocol.createRegisterIntent(account, allTypes, types);
- intent.putExtra(InvalidationIntentProtocol.EXTRA_CLIENT_NAME, TEST_CLIENT_NAME);
return intent;
}
@@ -841,7 +835,6 @@ public class InvalidationServiceTest extends ServiceTestCase<TestableInvalidatio
Account account, int[] objectSources, String[] objectNames) {
Intent intent = InvalidationIntentProtocol.createRegisterIntent(
account, objectSources, objectNames);
- intent.putExtra(InvalidationIntentProtocol.EXTRA_CLIENT_NAME, TEST_CLIENT_NAME);
return intent;
}