--- a/mobile/android/app/src/test/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetrySyncEventPingBuilderTest.java
+++ b/mobile/android/app/src/test/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetrySyncEventPingBuilderTest.java
@@ -1,43 +1,43 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.telemetry.pingbuilders;
import android.os.Bundle;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.telemetry.TelemetryContract;
@RunWith(TestRunner.class)
public class TelemetrySyncEventPingBuilderTest {
@Test
public void testGeneralShape() throws Exception {
JSONArray payload = buildPayloadArray(123456L, "sync", "object", "method", null, null);
- Assert.assertArrayEquals(new Object[] {123456L, "sync", "method", "object"}, payload.toArray());
+ Assert.assertEquals("[123456,\"sync\",\"method\",\"object\"]", payload.toString());
payload = buildPayloadArray(123456L, "sync", "object", "method", "value", null);
- Assert.assertArrayEquals(new Object[] {123456L, "sync", "method", "object", "value"}, payload.toArray());
+ Assert.assertEquals("[123456,\"sync\",\"method\",\"object\",\"value\"}]", payload.toString());
Bundle extra = new Bundle();
extra.putString("extra-key", "extra-value");
payload = buildPayloadArray(123456L, "sync", "object", "method", null, extra);
Assert.assertEquals("[123456,\"sync\",\"method\",\"object\",null,{\"extra-key\":\"extra-value\"}]",
- payload.toJSONString());
+ payload.toString());
payload = buildPayloadArray(123456L, "sync", "object", "method", "value", extra);
Assert.assertEquals("[123456,\"sync\",\"method\",\"object\",\"value\",{\"extra-key\":\"extra-value\"}]",
- payload.toJSONString());
+ payload.toString());
}
@Test(expected = IllegalStateException.class)
public void testNullTimestamp() throws Exception {
buildPayloadArray(null, "category", "object", "method", null, null);
}
@Test(expected = IllegalStateException.class)
--- a/mobile/android/app/src/test/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetrySyncPingBuilderTest.java
+++ b/mobile/android/app/src/test/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetrySyncPingBuilderTest.java
@@ -2,18 +2,17 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.telemetry.pingbuilders;
import android.os.Bundle;
import android.os.Parcelable;
import org.json.JSONException;
-
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.synchronizer.StoreBatchTracker;
import org.mozilla.gecko.sync.telemetry.TelemetryStageCollector;
import org.mozilla.gecko.telemetry.TelemetryLocalPing;
@@ -87,17 +86,17 @@ public class TelemetrySyncPingBuilderTes
stage2.inboundStored = 1;
stages.put("testing2", stage2);
TelemetryLocalPing localPing = builder
.setStages(stages)
.build();
ExtendedJSONObject payload = localPing.getPayload();
- assertEquals(2, payload.getArray("engines").size());
+ assertEquals(2, payload.getArray("engines").length());
ExtendedJSONObject engine = (ExtendedJSONObject) payload.getArray("engines").get(0);
assertEquals("testing", engine.getString("name"));
assertEquals(Long.valueOf(5L), engine.getLong("took"));
ExtendedJSONObject inbound = engine.getObject("incoming");
assertEquals(Integer.valueOf(stage.inbound), inbound.getIntegerSafely("applied"));
assertEquals(Integer.valueOf(stage.inboundStored), inbound.getIntegerSafely("succeeded"));
@@ -106,20 +105,20 @@ public class TelemetrySyncPingBuilderTes
ExtendedJSONObject error = engine.getObject("failureReason");
assertEquals("unexpectederror", error.getString("name"));
assertEquals("test", error.getString("error"));
ExtendedJSONObject validation = engine.getObject("validation");
assertEquals(stage.validation.getLong("took"), validation.getLong("took"));
assertEquals(stage.validation.getLong("checked"), validation.getLong("checked"));
- assertEquals(0, stage.validation.getArray("problems").size());
+ assertEquals(0, stage.validation.getArray("problems").length());
JSONArray outgoing = engine.getArray("outgoing");
- assertEquals(outgoing.size(), 3);
+ assertEquals(outgoing.length(), 3);
ExtendedJSONObject firstBatch = (ExtendedJSONObject) outgoing.get(0);
assertEquals(firstBatch.getLong("sent", -1), 1);
assertEquals(firstBatch.getLong("failed", -1), 1);
ExtendedJSONObject secondBatch = (ExtendedJSONObject) outgoing.get(1);
assertEquals(secondBatch.getLong("sent", -1), 9);
assertFalse(secondBatch.containsKey("failed"));
@@ -153,32 +152,32 @@ public class TelemetrySyncPingBuilderTes
devices.add(device);
// Test with only one device
payload = builder
.setDevices(devices)
.build()
.getPayload();
JSONArray devicesJSON = payload.getArray("devices");
- assertEquals(1, devicesJSON.size());
+ assertEquals(1, devicesJSON.length());
assertDevice((ExtendedJSONObject) devicesJSON.get(0), "Android", "53.0a1", "80daf12dsadsa4236914cff2cc6e9d0f80a965380e2cf8e976e4004ead887521b5d9");
device = new Bundle();
device.putString("os", "iOS");
device.putString("version", "8.0");
device.putString("id", "fa813452774b3cdc8f5f73290b5346df800f644b7b92a1ab94b6e2af748d261362");
devices.add(device);
// Test with more than one device
payload = builder
.setDevices(devices)
.build()
.getPayload();
devicesJSON = payload.getArray("devices");
- assertEquals(2, devicesJSON.size());
+ assertEquals(2, devicesJSON.length());
assertDevice((ExtendedJSONObject) devicesJSON.get(0), "Android", "53.0a1", "80daf12dsadsa4236914cff2cc6e9d0f80a965380e2cf8e976e4004ead887521b5d9");
assertDevice((ExtendedJSONObject) devicesJSON.get(1), "iOS", "8.0", "fa813452774b3cdc8f5f73290b5346df800f644b7b92a1ab94b6e2af748d261362");
}
private void assertDevice(ExtendedJSONObject device, String os, String version, String id) throws JSONException {
assertEquals(os, device.getString("os"));
assertEquals(version, device.getString("version"));
assertEquals(id, device.getString("id"));
--- a/mobile/android/app/src/test/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetrySyncPingBundleBuilderTest.java
+++ b/mobile/android/app/src/test/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetrySyncPingBundleBuilderTest.java
@@ -3,17 +3,17 @@
package org.mozilla.gecko.telemetry.pingbuilders;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import org.json.JSONException;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.telemetry.TelemetryContract;
import org.mozilla.gecko.telemetry.TelemetryOutgoingPing;
@@ -163,33 +163,33 @@ public class TelemetrySyncPingBundleBuil
builder.setSyncStore(syncPings);
TelemetryOutgoingPing outgoingPing = builder.build();
// Ensure we have that one ping.
ExtendedJSONObject payload = outgoingPing.getPayload().getObject("payload");
assertEquals("schedule", payload.getString("why"));
JSONArray syncs = payload.getArray("syncs");
- assertEquals(1, syncs.size());
+ assertEquals(1, syncs.length());
assertSync((ExtendedJSONObject) syncs.get(0), 123L, true);
// Add another ping.
syncPings.storePing(new TelemetrySyncPingBuilder()
.setRestarted(false)
.setTook(321L)
.build()
);
builder.setSyncStore(syncPings);
// We should have two pings now.
outgoingPing = builder.build();
syncs = outgoingPing.getPayload()
.getObject("payload")
.getArray("syncs");
- assertEquals(2, syncs.size());
+ assertEquals(2, syncs.length());
assertSync((ExtendedJSONObject) syncs.get(0), 123L, true);
assertSync((ExtendedJSONObject) syncs.get(1), 321L, false);
// And add an event ping!
Bundle event = new Bundle();
event.putLong(TelemetryContract.KEY_EVENT_TIMESTAMP, 123456L);
event.putString(TelemetryContract.KEY_EVENT_CATEGORY, "sync");
event.putString(TelemetryContract.KEY_EVENT_OBJECT, "object");
@@ -204,19 +204,19 @@ public class TelemetrySyncPingBundleBuil
);
builder.setSyncEventStore(eventPings);
// We should have three pings now.
outgoingPing = builder.build();
JSONArray events = outgoingPing.getPayload()
.getObject("payload")
.getArray("events");
- assertEquals(1, events.size());
+ assertEquals(1, events.length());
Assert.assertEquals("[[123456,\"sync\",\"method\",\"object\",\"value\",{\"extra-key\":\"extra-value\"}]]",
- events.toJSONString());
+ events.toString());
}
private void assertSync(ExtendedJSONObject sync, long took, boolean restarted) throws JSONException {
assertEquals(Long.valueOf(took), sync.getLong("took"));
// Test that 'when' timestamp looks generally sane.
final long now = System.currentTimeMillis();
final long yearAgo = now - 1000L * 60 * 60 * 24 * 365;
--- a/mobile/android/app/src/test/java/org/mozilla/gecko/telemetry/stores/TestTelemetryJSONFilePingStore.java
+++ b/mobile/android/app/src/test/java/org/mozilla/gecko/telemetry/stores/TestTelemetryJSONFilePingStore.java
@@ -45,17 +45,16 @@ public class TestTelemetryJSONFilePingSt
testDir = tempDir.newFolder();
testStore = new TelemetryJSONFilePingStore(testDir, "");
}
private ExtendedJSONObject generateTelemetryPayload() {
final ExtendedJSONObject out = new ExtendedJSONObject();
out.put("str", "a String");
out.put("int", 42);
- out.put("null", (ExtendedJSONObject) null);
return out;
}
private void assertIsGeneratedPayload(final ExtendedJSONObject actual) throws Exception {
assertNull("Null field is null", actual.getObject("null"));
assertEquals("int field is correct", 42, (int) actual.getIntegerSafely("int"));
assertEquals("str field is correct", "a String", actual.getString("str"));
}
--- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserDatabaseHelper.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserDatabaseHelper.java
@@ -10,19 +10,20 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.json.JSONException;
import org.mozilla.apache.commons.codec.binary.Base32;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONObject;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.R;
import org.mozilla.gecko.annotation.RobocopTarget;
import org.mozilla.gecko.background.common.PrefsBranch;
import org.mozilla.gecko.db.BrowserContract.ActivityStreamBlocklist;
import org.mozilla.gecko.db.BrowserContract.Bookmarks;
import org.mozilla.gecko.db.BrowserContract.Combined;
import org.mozilla.gecko.db.BrowserContract.Favicons;
@@ -993,35 +994,42 @@ public class BrowserDatabaseHelper exten
}
final JSONArray visitsInHistoryExtensionDB = RepoUtils.getJSONArrayFromCursor(historyExtensionCursor, columnVisits);
if (visitsInHistoryExtensionDB == null) {
continue;
}
- final int histExtVisitCount = visitsInHistoryExtensionDB.size();
+ final int histExtVisitCount = visitsInHistoryExtensionDB.length();
debug("Inserting " + histExtVisitCount + " visits from history extension db for GUID: " + guid);
for (int i = 0; i < histExtVisitCount; i++) {
- final JSONObject visit = (JSONObject) visitsInHistoryExtensionDB.get(i);
+ final JSONObject visit;
+ try {
+ visit = (JSONObject) visitsInHistoryExtensionDB.get(i);
+ } catch (JSONException e) {
+ // This should happen, but let's just ignore this visit if it does.
+ // We really don't want to fail a migration.
+ continue;
+ }
// Sanity check.
if (visit == null) {
continue;
}
// Let's not rely on underlying data being correct, and guard against casting failures.
// Since we can't recover from this (other than ignoring this visit), let's not fail user's migration.
final Long date;
final Long visitType;
try {
date = (Long) visit.get("date");
visitType = (Long) visit.get("type");
- } catch (ClassCastException e) {
+ } catch (JSONException | ClassCastException e) {
continue;
}
// Sanity check our incoming data.
if (date == null || visitType == null) {
continue;
}
// Bind parameters use a 1-based index.
@@ -2212,17 +2220,17 @@ public class BrowserDatabaseHelper exten
} catch (Exception e) {
Log.e(LOGTAG, "Could not read sync SharedPreferences. Skipping bookmark version migration.", e);
return;
}
final SynchronizerConfiguration synchronizerConfiguration;
try {
synchronizerConfiguration = new SynchronizerConfiguration(new PrefsBranch(syncPrefs, "bookmarks."));
- } catch (IOException | NonObjectJSONException e) {
+ } catch (JSONException e) {
Log.e(LOGTAG, "Could not process sync SharedPreferences. Skipping bookmark version migration.", e);
return;
}
final long lastSyncTimestamp = synchronizerConfiguration.localBundle.getTimestamp();
Log.d(LOGTAG, "Bookmarks last synced: " + lastSyncTimestamp);
performBookmarkTimestampToVersionMigration(db, lastSyncTimestamp);
--- a/mobile/android/base/java/org/mozilla/gecko/telemetry/TelemetryBackgroundReceiver.java
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/TelemetryBackgroundReceiver.java
@@ -11,16 +11,18 @@ import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.Experiments;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.switchboard.SwitchBoard;
import org.mozilla.gecko.sync.telemetry.TelemetryContract;
import org.mozilla.gecko.telemetry.pingbuilders.TelemetrySyncEventPingBuilder;
import org.mozilla.gecko.telemetry.pingbuilders.TelemetrySyncPingBuilder;
import org.mozilla.gecko.telemetry.pingbuilders.TelemetrySyncPingBundleBuilder;
@@ -138,17 +140,22 @@ public class TelemetryBackgroundReceiver
final String deviceID = telemetryBundle.getString(TelemetryContract.KEY_LOCAL_DEVICE_ID);
// Transform incoming telemetry into a local ping of correct type (sync vs event).
final TelemetryLocalPing localPing;
final TelemetryPingStore telemetryStore;
switch (type) {
case TelemetryContract.KEY_TYPE_SYNC:
final ArrayList<Parcelable> devices = telemetryBundle.getParcelableArrayList(TelemetryContract.KEY_DEVICES);
- final Serializable error = telemetryBundle.getSerializable(TelemetryContract.KEY_ERROR);
+ JSONObject error = null;
+ try {
+ error = new JSONObject(telemetryBundle.getString(TelemetryContract.KEY_ERROR));
+ } catch (JSONException e) {
+ Log.e(LOG_TAG, "Error parsing 'error' json", e);
+ }
final Serializable stages = telemetryBundle.getSerializable(TelemetryContract.KEY_STAGES);
final long took = telemetryBundle.getLong(TelemetryContract.KEY_TOOK);
final boolean didRestart = telemetryBundle.getBoolean(TelemetryContract.KEY_RESTARTED);
telemetryStore = syncTelemetryStore;
TelemetrySyncPingBuilder localPingBuilder = new TelemetrySyncPingBuilder();
if (devices != null) {
--- a/mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetryCorePingBuilder.java
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetryCorePingBuilder.java
@@ -11,16 +11,17 @@ import android.content.SharedPreferences
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import android.util.Log;
+import org.json.JSONArray;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.Locales;
import org.mozilla.gecko.mma.MmaDelegate;
import org.mozilla.gecko.search.SearchEngine;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.telemetry.TelemetryOutgoingPing;
--- a/mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetryCrashPingBuilder.java
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetryCrashPingBuilder.java
@@ -3,16 +3,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mozilla.gecko.telemetry.pingbuilders;
import android.util.Log;
+import org.json.JSONException;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.util.StringUtils;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
@@ -85,17 +86,17 @@ public class TelemetryCrashPingBuilder e
try {
env = new ExtendedJSONObject(annotations.get("TelemetryEnvironment"));
final ExtendedJSONObject build = env.getObject("build");
if (build != null) {
architecture = build.getString("architecture");
xpcomAbi = build.getString("xpcomAbi");
}
- } catch (NonObjectJSONException | IOException e) {
+ } catch (JSONException | NonObjectJSONException e) {
Log.w(LOGTAG, "Couldn't parse the telemetry environment, the ping will be incomplete");
}
if (env != null) {
payload.put("environment", env);
}
payload.put("payload", createPayloadNode(crashId, annotations));
@@ -136,17 +137,17 @@ public class TelemetryCrashPingBuilder e
node.put("hasCrashEnvironment", true);
node.put("crashId", crashId);
node.put("minidumpSha256Hash", annotations.get("MinidumpSha256Hash"));
node.put("processType", "main");
try {
final ExtendedJSONObject stackTraces = new ExtendedJSONObject(annotations.get("StackTraces"));
node.put("stackTraces", stackTraces);
- } catch (NonObjectJSONException | IOException e) {
+ } catch (JSONException e) {
Log.w(LOGTAG, "Couldn't parse the stack traces, the ping will be incomplete");
}
// Assemble the payload metadata
node.put("metadata", createMetadataNode(annotations));
return node;
}
--- a/mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetrySyncEventPingBuilder.java
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetrySyncEventPingBuilder.java
@@ -1,17 +1,17 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.telemetry.pingbuilders;
import android.os.Bundle;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.telemetry.TelemetryContract;
import org.mozilla.gecko.telemetry.TelemetryLocalPing;
public class TelemetrySyncEventPingBuilder extends TelemetryLocalPingBuilder {
@SuppressWarnings("unchecked")
public TelemetrySyncEventPingBuilder fromEventTelemetry(Bundle data) {
@@ -24,28 +24,28 @@ public class TelemetrySyncEventPingBuild
if (timestamp == -1L || category == null || object == null || method == null) {
throw new IllegalStateException("Bundle should be well formed.");
}
final JSONArray event = new JSONArray();
// Events are serialized as arrays when sending the sync ping. The order of the following
// statements SHOULD NOT be changed unless the telemetry server specification changes.
- event.add(timestamp);
- event.add(category);
- event.add(method);
- event.add(object);
+ event.put(timestamp);
+ event.put(category);
+ event.put(method);
+ event.put(object);
if (value != null || extra != null) {
- event.add(value);
+ event.put(value);
if (extra != null) {
final ExtendedJSONObject extraJSON = new ExtendedJSONObject();
for (final String k : extra.keySet()) {
extraJSON.put(k, extra.getString(k));
}
- event.add(extraJSON);
+ event.put(extraJSON);
}
}
/**
* Note: {@link org.mozilla.gecko.telemetry.TelemetryOutgoingPing#getPayload()}
* returns ExtendedJSONObject. Wrap our JSONArray into the payload JSON object.
*/
payload.put("event", event);
return this;
--- a/mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetrySyncPingBuilder.java
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetrySyncPingBuilder.java
@@ -6,18 +6,18 @@
package org.mozilla.gecko.telemetry.pingbuilders;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONObject;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.synchronizer.StoreBatchTracker;
import org.mozilla.gecko.sync.telemetry.TelemetryContract;
import org.mozilla.gecko.sync.telemetry.TelemetryStageCollector;
import org.mozilla.gecko.telemetry.TelemetryLocalPing;
import java.io.Serializable;
import java.util.ArrayList;
@@ -101,17 +101,17 @@ public class TelemetrySyncPingBuilder ex
if (batch.sent != 0) {
o.put("sent", (long) batch.sent);
}
if (batch.failed != 0) {
o.put("failed", (long) batch.failed);
}
addUnchecked(arr, o);
}
- return arr.size() == 0 ? null : arr;
+ return arr.length() == 0 ? null : arr;
}
public TelemetrySyncPingBuilder setRestarted(boolean didRestart) {
if (!didRestart) {
return this;
}
payload.put("restarted", true);
@@ -127,41 +127,41 @@ public class TelemetrySyncPingBuilder ex
deviceJSON.put("os", deviceBundle.getString(TelemetryContract.KEY_DEVICE_OS));
deviceJSON.put("version", deviceBundle.getString(TelemetryContract.KEY_DEVICE_VERSION));
deviceJSON.put("id", deviceBundle.getString(TelemetryContract.KEY_DEVICE_ID));
addUnchecked(devicesJSON, deviceJSON);
}
- if (devicesJSON.size() > 0) {
+ if (devicesJSON.length() > 0) {
payload.put("devices", devicesJSON);
}
return this;
}
- public TelemetrySyncPingBuilder setError(@NonNull Serializable error) {
- payload.put("failureReason", new ExtendedJSONObject((JSONObject) error));
+ public TelemetrySyncPingBuilder setError(@NonNull JSONObject error) {
+ payload.put("failureReason", new ExtendedJSONObject(error));
return this;
}
public TelemetrySyncPingBuilder setTook(long took) {
payload.put("took", took);
return this;
}
@Override
public TelemetryLocalPing build() {
payload.put("when", System.currentTimeMillis());
return new TelemetryLocalPing(payload, docID);
}
@SuppressWarnings("unchecked")
private static void addUnchecked(final JSONArray list, final ExtendedJSONObject obj) {
- list.add(obj);
+ list.put(obj);
}
/**
* We broadcast this data via LocalBroadcastManager and control both sides of this code, so it
* is acceptable to do an unchecked cast.
*/
@SuppressWarnings("unchecked")
private static HashMap<String, TelemetryStageCollector> castSyncData(final Serializable data) {
--- a/mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetrySyncPingBundleBuilder.java
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetrySyncPingBundleBuilder.java
@@ -5,17 +5,17 @@
*/
package org.mozilla.gecko.telemetry.pingbuilders;
import android.os.Build;
import android.support.annotation.NonNull;
import android.util.Log;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.Locales;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.NonArrayJSONException;
import org.mozilla.gecko.telemetry.TelemetryOutgoingPing;
import org.mozilla.gecko.telemetry.TelemetryPing;
import org.mozilla.gecko.telemetry.stores.TelemetryPingStore;
@@ -122,35 +122,35 @@ public class TelemetrySyncPingBundleBuil
public TelemetrySyncPingBundleBuilder setSyncStore(TelemetryPingStore store) {
final JSONArray syncs = new JSONArray();
List<TelemetryPing> pings = store.getAllPings();
// Please note how we're not including constituent ping's docID in the final payload. This is
// unfortunate and causes some grief when managing local ping storage and uploads, but needs
// to be resolved beyond this individual client. See Bug 1369186.
for (TelemetryPing ping : pings) {
- syncs.add(ping.getPayload());
+ syncs.put(ping.getPayload());
}
- if (syncs.size() > 0) {
+ if (syncs.length() > 0) {
pingData.put("syncs", syncs);
}
return this;
}
@SuppressWarnings("unchecked")
public TelemetrySyncPingBundleBuilder setSyncEventStore(TelemetryPingStore store) {
final JSONArray events = new JSONArray();
List<TelemetryPing> pings = store.getAllPings();
for (TelemetryPing ping : pings) {
try {
- events.add(ping.getPayload().getArray("event"));
+ events.put(ping.getPayload().getArray("event"));
} catch (NonArrayJSONException ex) {
Log.e(LOG_TAG, "Invalid state: Non JSONArray for event payload.");
}
}
- if (events.size() > 0) {
+ if (events.length() > 0) {
pingData.put("events", events);
}
return this;
}
}
--- a/mobile/android/base/java/org/mozilla/gecko/telemetry/stores/TelemetryJSONFilePingStore.java
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/stores/TelemetryJSONFilePingStore.java
@@ -172,17 +172,17 @@ public class TelemetryJSONFilePingStore
if (obj == null) {
// We log in the method to get the JSONObject if we return null.
continue;
}
final ExtendedJSONObject payload;
try {
payload = new ExtendedJSONObject(obj.getString(KEY_PAYLOAD));
- } catch (IOException | JSONException | NonObjectJSONException e) {
+ } catch (JSONException e) {
Log.w(LOGTAG, "Bad json in ping. Ignoring.");
continue;
}
try {
final String url = obj.getString(KEY_URL_PATH);
out.add(new TelemetryOutgoingPing(url, payload, file.getName()));
} catch (JSONException e) {
--- a/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/db/TestAndroidBrowserBookmarksRepository.java
+++ b/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/db/TestAndroidBrowserBookmarksRepository.java
@@ -1,16 +1,16 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.background.db;
import java.util.ArrayList;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.mozilla.gecko.background.sync.helpers.BookmarkHelpers;
import org.mozilla.gecko.background.sync.helpers.ExpectFetchDelegate;
import org.mozilla.gecko.background.sync.helpers.ExpectFetchSinceDelegate;
import org.mozilla.gecko.background.sync.helpers.ExpectFinishDelegate;
import org.mozilla.gecko.background.sync.helpers.ExpectInvalidTypeStoreDelegate;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.repositories.NullCursorException;
--- a/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/db/TestAndroidBrowserHistoryRepository.java
+++ b/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/db/TestAndroidBrowserHistoryRepository.java
@@ -1,16 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.background.db;
import java.util.ArrayList;
-import org.json.simple.JSONObject;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.mozilla.gecko.background.sync.helpers.ExpectFetchDelegate;
import org.mozilla.gecko.background.sync.helpers.HistoryHelpers;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.repositories.NullCursorException;
import org.mozilla.gecko.sync.repositories.Repository;
import org.mozilla.gecko.sync.repositories.RepositorySession;
import org.mozilla.gecko.sync.repositories.android.HistoryDataAccessor;
@@ -50,114 +51,114 @@ public class TestAndroidBrowserHistoryRe
}
@Override
protected DataAccessor getDataAccessor() {
return new HistoryDataAccessor(getApplicationContext());
}
@Override
- public void testFetchAll() {
+ public void testFetchAll() throws JSONException {
Record[] expected = new Record[2];
expected[0] = HistoryHelpers.createHistory3();
expected[1] = HistoryHelpers.createHistory2();
basicFetchAllTest(expected);
}
/*
* Test storing identical records with different guids.
* For bookmarks identical is defined by the following fields
* being the same: title, uri, type, parentName
*/
@Override
- public void testStoreIdenticalExceptGuid() {
+ public void testStoreIdenticalExceptGuid() throws JSONException {
storeIdenticalExceptGuid(HistoryHelpers.createHistory1());
}
@Override
- public void testCleanMultipleRecords() {
+ public void testCleanMultipleRecords() throws JSONException {
cleanMultipleRecords(
HistoryHelpers.createHistory1(),
HistoryHelpers.createHistory2(),
HistoryHelpers.createHistory3(),
HistoryHelpers.createHistory4(),
HistoryHelpers.createHistory5()
);
}
@Override
- public void testFetchSinceOneRecord() {
+ public void testFetchSinceOneRecord() throws JSONException {
fetchSinceOneRecord(HistoryHelpers.createHistory1(),
HistoryHelpers.createHistory2());
}
@Override
- public void testFetchSinceReturnNoRecords() {
+ public void testFetchSinceReturnNoRecords() throws JSONException {
fetchSinceReturnNoRecords(HistoryHelpers.createHistory3());
}
@Override
- public void testFetchOneRecordByGuid() {
+ public void testFetchOneRecordByGuid() throws JSONException {
fetchOneRecordByGuid(HistoryHelpers.createHistory1(),
HistoryHelpers.createHistory2());
}
@Override
- public void testFetchMultipleRecordsByGuids() {
+ public void testFetchMultipleRecordsByGuids() throws JSONException {
HistoryRecord record0 = HistoryHelpers.createHistory1();
HistoryRecord record1 = HistoryHelpers.createHistory2();
HistoryRecord record2 = HistoryHelpers.createHistory3();
fetchMultipleRecordsByGuids(record0, record1, record2);
}
@Override
- public void testFetchNoRecordByGuid() {
+ public void testFetchNoRecordByGuid() throws JSONException {
fetchNoRecordByGuid(HistoryHelpers.createHistory1());
}
@Override
- public void testWipe() {
+ public void testWipe() throws JSONException {
doWipe(HistoryHelpers.createHistory2(), HistoryHelpers.createHistory3());
}
@Override
- public void testStore() {
+ public void testStore() throws JSONException {
basicStoreTest(HistoryHelpers.createHistory1());
}
@Override
- public void testRemoteNewerTimeStamp() {
+ public void testRemoteNewerTimeStamp() throws JSONException {
HistoryRecord local = HistoryHelpers.createHistory1();
HistoryRecord remote = HistoryHelpers.createHistory2();
remoteNewerTimeStamp(local, remote);
}
@Override
- public void testLocalNewerTimeStamp() {
+ public void testLocalNewerTimeStamp() throws JSONException {
HistoryRecord local = HistoryHelpers.createHistory1();
HistoryRecord remote = HistoryHelpers.createHistory2();
localNewerTimeStamp(local, remote);
}
@Override
- public void testDeleteRemoteNewer() {
+ public void testDeleteRemoteNewer() throws JSONException {
HistoryRecord local = HistoryHelpers.createHistory1();
HistoryRecord remote = HistoryHelpers.createHistory2();
deleteRemoteNewer(local, remote);
}
@Override
- public void testDeleteLocalNewer() {
+ public void testDeleteLocalNewer() throws JSONException {
HistoryRecord local = HistoryHelpers.createHistory1();
HistoryRecord remote = HistoryHelpers.createHistory2();
deleteLocalNewer(local, remote);
}
@Override
- public void testDeleteRemoteLocalNonexistent() {
+ public void testDeleteRemoteLocalNonexistent() throws JSONException {
deleteRemoteLocalNonexistent(HistoryHelpers.createHistory2());
}
/**
* Verifies that two history records with the same URI but different
* titles will be reconciled locally.
*/
public void testRecordStringCollisionAndEquality() {
@@ -193,76 +194,75 @@ public class TestAndroidBrowserHistoryRe
assertFalse(record1.equalPayloads(record0));
assertFalse(record1.equalPayloads(record2));
}
/*
* Tests for adding some visits to a history record
* and doing a fetch.
*/
- @SuppressWarnings("unchecked")
- public void testAddOneVisit() throws Exception {
+ public void testAddOneVisit() throws JSONException {
+
final RepositorySession session = createAndBeginSession();
HistoryRecord record0 = HistoryHelpers.createHistory3();
performWait(storeRunnable(session, record0));
// Add one visit to the count and put in a new
// last visited date.
ContentValues cv = new ContentValues();
- int visits = record0.visits.size() + 1;
+ int visits = record0.visits.length() + 1;
long newVisitTime = System.currentTimeMillis();
cv.put(BrowserContract.History.VISITS, visits);
cv.put(BrowserContract.History.DATE_LAST_VISITED, newVisitTime);
final DataAccessor dataAccessor = getDataAccessor();
dataAccessor.updateByGuid(record0.guid, cv);
// Add expected visit to record for verification.
JSONObject expectedVisit = new JSONObject();
expectedVisit.put("date", newVisitTime * 1000); // Microseconds.
expectedVisit.put("type", 1L);
- record0.visits.add(expectedVisit);
+ record0.visits.put(expectedVisit);
performWait(fetchRunnable(session, new String[] { record0.guid }, new ExpectFetchDelegate(new Record[] { record0 })));
closeDataAccessor(dataAccessor);
}
- @SuppressWarnings("unchecked")
- public void testAddMultipleVisits() throws Exception {
+ public void testAddMultipleVisits() throws JSONException {
final RepositorySession session = createAndBeginSession();
HistoryRecord record0 = HistoryHelpers.createHistory4();
performWait(storeRunnable(session, record0));
// Add three visits to the count and put in a new
// last visited date.
ContentValues cv = new ContentValues();
- int visits = record0.visits.size() + 3;
+ int visits = record0.visits.length() + 3;
long newVisitTime = System.currentTimeMillis();
cv.put(BrowserContract.History.VISITS, visits);
cv.put(BrowserContract.History.DATE_LAST_VISITED, newVisitTime);
final DataAccessor dataAccessor = getDataAccessor();
dataAccessor.updateByGuid(record0.guid, cv);
// Now shift to microsecond timing for visits.
long newMicroVisitTime = newVisitTime * 1000;
// Add expected visits to record for verification
JSONObject expectedVisit = new JSONObject();
expectedVisit.put("date", newMicroVisitTime);
expectedVisit.put("type", 1L);
- record0.visits.add(expectedVisit);
+ record0.visits.put(expectedVisit);
expectedVisit = new JSONObject();
expectedVisit.put("date", newMicroVisitTime - 1000);
expectedVisit.put("type", 1L);
- record0.visits.add(expectedVisit);
+ record0.visits.put(expectedVisit);
expectedVisit = new JSONObject();
expectedVisit.put("date", newMicroVisitTime - 2000);
expectedVisit.put("type", 1L);
- record0.visits.add(expectedVisit);
+ record0.visits.put(expectedVisit);
ExpectFetchDelegate delegate = new ExpectFetchDelegate(new Record[] { record0 });
performWait(fetchRunnable(session, new String[] { record0.guid }, delegate));
Record fetched = delegate.records.get(0);
assertTrue(record0.equalPayloads(fetched));
closeDataAccessor(dataAccessor);
}
@@ -304,17 +304,17 @@ public class TestAndroidBrowserHistoryRe
all.close();
// But aren't returned by fetching.
performWait(fetchAllRunnable(session, new Record[] {}));
session.abort();
}
- public void testSqlInjectPurgeDelete() throws Exception {
+ public void testSqlInjectPurgeDelete() throws JSONException {
// Some setup.
RepositorySession session = createAndBeginSession();
final DataAccessor db = getDataAccessor();
try {
ContentValues cv = new ContentValues();
cv.put(BrowserContract.SyncColumns.IS_DELETED, 1);
@@ -389,17 +389,17 @@ public class TestAndroidBrowserHistoryRe
protected Cursor getAllHistory() {
Context context = getApplicationContext();
Cursor cur = context.getContentResolver().query(BrowserContractHelpers.HISTORY_CONTENT_URI,
BrowserContractHelpers.HistoryColumns, null, null, null);
return cur;
}
- public void testDataAccessorBulkInsert() throws Exception {
+ public void testDataAccessorBulkInsert() throws NullCursorException, JSONException {
final HistoryRepositorySession session = (HistoryRepositorySession) createAndBeginSession();
final HistoryDataAccessor db = new HistoryDataAccessor(getApplicationContext());
ArrayList<HistoryRecord> records = new ArrayList<HistoryRecord>();
records.add(HistoryHelpers.createHistory1());
records.add(HistoryHelpers.createHistory2());
records.add(HistoryHelpers.createHistory3());
db.bulkInsert(records);
--- a/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/db/TestBookmarks.java
+++ b/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/db/TestBookmarks.java
@@ -4,17 +4,18 @@
package org.mozilla.gecko.background.db;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
+import org.json.JSONException;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.helpers.AndroidSyncTestCase;
import org.mozilla.gecko.background.sync.helpers.BookmarkHelpers;
import org.mozilla.gecko.background.sync.helpers.SimpleSuccessFetchDelegate;
import org.mozilla.gecko.background.sync.helpers.SimpleSuccessFinishDelegate;
import org.mozilla.gecko.background.sync.helpers.SimpleSuccessStoreDelegate;
import org.mozilla.gecko.db.BrowserContract;
@@ -63,17 +64,17 @@ public class TestBookmarks extends Andro
assertTrue(pinned.contains("dapinneditem"));
// … but not when we fetch.
final ArrayList<String> guids = fetchGUIDs(repo);
assertFalse(guids.contains(Bookmarks.PINNED_FOLDER_GUID));
assertFalse(guids.contains("dapinneditem"));
}
- public void testRetrieveFolderHasAccurateChildren() throws SyncException {
+ public void testRetrieveFolderHasAccurateChildren() throws SyncException, JSONException {
BookmarksRepository repo = new BookmarksRepository();
final long now = System.currentTimeMillis();
final String folderGUID = "eaaaaaaaafff";
BookmarkRecord folder = new BookmarkRecord(folderGUID, "bookmarks", now - 5, false);
BookmarkRecord bookmarkA = new BookmarkRecord("daaaaaaaaaaa", "bookmarks", now - 1, false);
BookmarkRecord bookmarkB = new BookmarkRecord("baaaaaaaabbb", "bookmarks", now - 3, false);
@@ -173,17 +174,17 @@ public class TestBookmarks extends Andro
// Ensure that records are ordered even if we re-process the folder.
wipe();
storeRecordsInSession(repo, parentLast, null);
folder.lastModified++;
storeRecordsInSession(repo, folderOnly, null);
assertChildrenAreOrdered(repo, folderGUID, children);
}
- public void testMergeFoldersPreservesSaneOrder() throws SyncException {
+ public void testMergeFoldersPreservesSaneOrder() throws SyncException, JSONException {
BookmarksRepository repo = new BookmarksRepository();
final long now = System.currentTimeMillis();
final String folderGUID = "mobile";
wipe();
final long mobile = setUpFennecMobileRecord();
@@ -193,18 +194,18 @@ public class TestBookmarks extends Andro
// Add some, as Fennec would.
fennecAddBookmark("Bookmark One", "http://example.com/fennec/One");
fennecAddBookmark("Bookmark Two", "http://example.com/fennec/Two");
Logger.debug(getName(), "Fetching children...");
JSONArray folderChildren = fetchChildrenForGUID(repo, folderGUID);
assertTrue(folderChildren != null);
- Logger.debug(getName(), "Children are " + folderChildren.toJSONString());
- assertEquals(2, folderChildren.size());
+ Logger.debug(getName(), "Children are " + folderChildren.toString());
+ assertEquals(2, folderChildren.length());
String guidOne = (String) folderChildren.get(0);
String guidTwo = (String) folderChildren.get(1);
// Make sure positions were saved.
assertChildrenAreDirect(mobile, new String[] {
guidOne,
guidTwo
});
@@ -265,17 +266,17 @@ public class TestBookmarks extends Andro
});
}
/**
* Apply a folder record whose children array is already accurately
* stored in the database. Verify that the parent folder is not flagged
* for reupload (i.e., that its modified time is *ahem* unmodified).
*/
- public void testNoReorderingMeansNoReupload() throws SyncException {
+ public void testNoReorderingMeansNoReupload() throws SyncException, JSONException {
BookmarksRepository repo = new BookmarksRepository();
final long now = System.currentTimeMillis();
final String folderGUID = "eaaaaaaaafff";
BookmarkRecord folder = new BookmarkRecord(folderGUID, "bookmarks", now -5, false);
BookmarkRecord bookmarkA = new BookmarkRecord("daaaaaaaaaaa", "bookmarks", now -1, false);
BookmarkRecord bookmarkB = new BookmarkRecord("baaaaaaaabbb", "bookmarks", now -3, false);
@@ -349,17 +350,17 @@ public class TestBookmarks extends Andro
assertFalse(tracked.contains(folderGUID));
}
/**
* Exercise the deletion of folders when their children have not been
* marked as deleted. In a database with constraints, this would fail
* if we simply deleted the records, so we move them first.
*/
- public void testFolderDeletionOrphansChildren() throws SyncException {
+ public void testFolderDeletionOrphansChildren() throws SyncException, JSONException {
BookmarksRepository repo = new BookmarksRepository();
long now = System.currentTimeMillis();
// Add a folder and four children.
final String folderGUID = "eaaaaaaaafff";
BookmarkRecord folder = new BookmarkRecord(folderGUID, "bookmarks", now -5, false);
BookmarkRecord bookmarkA = new BookmarkRecord("daaaaaaaaaaa", "bookmarks", now -1, false);
@@ -696,17 +697,17 @@ public class TestBookmarks extends Andro
final String guid) throws SyncException {
return fetchGUID(repo, guid).children;
}
@SuppressWarnings("unchecked")
protected static JSONArray childrenFromRecords(BookmarkRecord... records) {
JSONArray children = new JSONArray();
for (BookmarkRecord record : records) {
- children.add(record.guid);
+ children.put(record.guid);
}
return children;
}
protected void updateRow(ContentValues values) {
Uri uri = BrowserContractHelpers.BOOKMARKS_CONTENT_URI;
final String where = BrowserContract.Bookmarks.GUID + " = ?";
@@ -869,37 +870,37 @@ public class TestBookmarks extends Andro
return dataAccessor;
}
protected void wipe() {
Logger.debug(getName(), "Wiping.");
getDataAccessor().wipe();
}
- protected void assertChildrenAreOrdered(BookmarksRepository repo, String guid, Record[] expected) throws SyncException {
+ protected void assertChildrenAreOrdered(BookmarksRepository repo, String guid, Record[] expected) throws SyncException, JSONException {
Logger.debug(getName(), "Fetching children...");
JSONArray folderChildren = fetchChildrenForGUID(repo, guid);
assertTrue(folderChildren != null);
- Logger.debug(getName(), "Children are " + folderChildren.toJSONString());
- assertEquals(expected.length, folderChildren.size());
+ Logger.debug(getName(), "Children are " + folderChildren.toString());
+ assertEquals(expected.length, folderChildren.length());
for (int i = 0; i < expected.length; ++i) {
assertEquals(expected[i].guid, ((String) folderChildren.get(i)));
}
}
protected void assertChildrenAreUnordered(BookmarksRepository repo, String guid, Record[] expected) throws SyncException {
Logger.debug(getName(), "Fetching children...");
JSONArray folderChildren = fetchChildrenForGUID(repo, guid);
assertTrue(folderChildren != null);
- Logger.debug(getName(), "Children are " + folderChildren.toJSONString());
- assertEquals(expected.length, folderChildren.size());
+ Logger.debug(getName(), "Children are " + folderChildren.toString());
+ assertEquals(expected.length, folderChildren.length());
for (Record record : expected) {
- folderChildren.contains(record.guid);
+ Utils.jsonArrayHas(folderChildren, record.guid);
}
}
/**
* Return a sequence of children GUIDs for the provided folder ID.
*/
protected ArrayList<String> fetchChildrenDirect(long id) {
Logger.debug(getName(), "Fetching children directly from DB...");
--- a/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/db/TestFennecTabsRepositorySession.java
+++ b/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/db/TestFennecTabsRepositorySession.java
@@ -1,14 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.background.db;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.mozilla.gecko.background.helpers.AndroidSyncTestCase;
import org.mozilla.gecko.background.sync.helpers.ExpectFetchDelegate;
import org.mozilla.gecko.background.sync.helpers.SessionTestHelper;
import org.mozilla.gecko.background.testhelpers.MockClientsDataDelegate;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.BrowserContract.Clients;
import org.mozilla.gecko.sync.SessionCreateException;
import org.mozilla.gecko.sync.repositories.NoContentProviderException;
@@ -162,28 +162,28 @@ public class TestFennecTabsRepositorySes
protected Tab testTab1;
protected Tab testTab2;
protected Tab testTab3;
@SuppressWarnings("unchecked")
private void insertSomeTestTabs(ContentProviderClient tabsClient) throws RemoteException {
final JSONArray history1 = new JSONArray();
- history1.add("http://test.com/test1.html");
+ history1.put("http://test.com/test1.html");
testTab1 = new Tab("test title 1", "http://test.com/test1.png", history1, 1000);
final JSONArray history2 = new JSONArray();
- history2.add("http://test.com/test2.html#1");
- history2.add("http://test.com/test2.html#2");
- history2.add("http://test.com/test2.html#3");
+ history2.put("http://test.com/test2.html#1");
+ history2.put("http://test.com/test2.html#2");
+ history2.put("http://test.com/test2.html#3");
testTab2 = new Tab("test title 2", "http://test.com/test2.png", history2, 2000);
final JSONArray history3 = new JSONArray();
- history3.add("http://test.com/test3.html#1");
- history3.add("http://test.com/test3.html#2");
+ history3.put("http://test.com/test3.html#1");
+ history3.put("http://test.com/test3.html#2");
testTab3 = new Tab("test title 3", "http://test.com/test3.png", history3, 3000);
tabsClient.insert(BrowserContractHelpers.TABS_CONTENT_URI, testTab1.toContentValues(TEST_CLIENT_GUID, 0));
tabsClient.insert(BrowserContractHelpers.TABS_CONTENT_URI, testTab2.toContentValues(TEST_CLIENT_GUID, 1));
tabsClient.insert(BrowserContractHelpers.TABS_CONTENT_URI, testTab3.toContentValues(TEST_CLIENT_GUID, 2));
}
protected TabsRecord insertTestTabsAndExtractTabsRecord() throws RemoteException {
--- a/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/db/ThreadedRepositoryTestCase.java
+++ b/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/db/ThreadedRepositoryTestCase.java
@@ -1,15 +1,16 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.background.db;
import java.util.concurrent.ExecutorService;
+import org.json.JSONException;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.helpers.AndroidSyncTestCase;
import org.mozilla.gecko.background.sync.helpers.DefaultCleanDelegate;
import org.mozilla.gecko.background.sync.helpers.DefaultFetchDelegate;
import org.mozilla.gecko.background.sync.helpers.DefaultFinishDelegate;
import org.mozilla.gecko.background.sync.helpers.DefaultStoreDelegate;
import org.mozilla.gecko.background.sync.helpers.ExpectFetchDelegate;
import org.mozilla.gecko.background.sync.helpers.ExpectFetchSinceDelegate;
@@ -228,31 +229,31 @@ public abstract class ThreadedRepository
protected abstract Repository getRepository();
protected abstract DataAccessor getDataAccessor();
protected static void doStore(RepositorySession session, Record[] records) {
performWait(storeManyRunnable(session, records));
}
// Tests to implement
- public abstract void testFetchAll();
- public abstract void testFetchSinceOneRecord();
- public abstract void testFetchSinceReturnNoRecords();
- public abstract void testFetchOneRecordByGuid();
- public abstract void testFetchMultipleRecordsByGuids();
- public abstract void testFetchNoRecordByGuid();
- public abstract void testWipe();
- public abstract void testStore();
- public abstract void testRemoteNewerTimeStamp();
- public abstract void testLocalNewerTimeStamp();
- public abstract void testDeleteRemoteNewer();
- public abstract void testDeleteLocalNewer();
- public abstract void testDeleteRemoteLocalNonexistent();
- public abstract void testStoreIdenticalExceptGuid();
- public abstract void testCleanMultipleRecords();
+ public abstract void testFetchAll() throws JSONException;
+ public abstract void testFetchSinceOneRecord() throws JSONException;
+ public abstract void testFetchSinceReturnNoRecords() throws JSONException;
+ public abstract void testFetchOneRecordByGuid() throws JSONException;
+ public abstract void testFetchMultipleRecordsByGuids() throws JSONException;
+ public abstract void testFetchNoRecordByGuid() throws JSONException;
+ public abstract void testWipe() throws JSONException;
+ public abstract void testStore() throws JSONException;
+ public abstract void testRemoteNewerTimeStamp() throws JSONException;
+ public abstract void testLocalNewerTimeStamp() throws JSONException;
+ public abstract void testDeleteRemoteNewer() throws JSONException;
+ public abstract void testDeleteLocalNewer() throws JSONException;
+ public abstract void testDeleteRemoteLocalNonexistent() throws JSONException;
+ public abstract void testStoreIdenticalExceptGuid() throws JSONException;
+ public abstract void testCleanMultipleRecords() throws JSONException;
/*
* Test abstractions
*/
protected void basicStoreTest(Record record) {
final RepositorySession session = createAndBeginSession();
performWait(storeRunnable(session, record));
--- a/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/sync/TestClientsStage.java
+++ b/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/sync/TestClientsStage.java
@@ -1,14 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.background.sync;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.mozilla.gecko.background.helpers.AndroidSyncTestCase;
import org.mozilla.gecko.background.testhelpers.DefaultGlobalSessionCallback;
import org.mozilla.gecko.background.testhelpers.MockClientsDataDelegate;
import org.mozilla.gecko.background.testhelpers.MockSharedPreferences;
import org.mozilla.gecko.sync.GlobalSession;
import org.mozilla.gecko.sync.SyncConfiguration;
import org.mozilla.gecko.sync.crypto.KeyBundle;
import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
--- a/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/sync/helpers/BookmarkHelpers.java
+++ b/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/sync/helpers/BookmarkHelpers.java
@@ -1,14 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.background.sync.helpers;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
public class BookmarkHelpers {
private static String mobileFolderGuid = "mobile";
private static String mobileFolderName = "mobile";
private static String topFolderGuid = Utils.generateGuid();
@@ -36,95 +36,90 @@ public class BookmarkHelpers {
public static BookmarkRecord createBookmarkInMobileFolder2() {
BookmarkRecord rec = createBookmark2();
rec.guid = Utils.generateGuid();
rec.parentID = mobileFolderGuid;
rec.parentName = mobileFolderName;
return rec;
}
- @SuppressWarnings("unchecked")
public static BookmarkRecord createBookmark1() {
BookmarkRecord record = new BookmarkRecord();
JSONArray tags = new JSONArray();
- tags.add("tag1");
- tags.add("tag2");
- tags.add("tag3");
+ tags.put("tag1");
+ tags.put("tag2");
+ tags.put("tag3");
record.guid = bmk1Guid;
record.title = "Foo!!!";
record.bookmarkURI = "http://foo.bar.com";
record.description = "This is a description for foo.bar.com";
record.tags = tags;
record.keyword = "fooooozzzzz";
record.parentID = topFolderGuid;
record.parentName = topFolderName;
record.type = "bookmark";
return record;
}
- @SuppressWarnings("unchecked")
public static BookmarkRecord createBookmark2() {
BookmarkRecord record = new BookmarkRecord();
JSONArray tags = new JSONArray();
- tags.add("tag1");
- tags.add("tag2");
+ tags.put("tag1");
+ tags.put("tag2");
record.guid = bmk2Guid;
record.title = "Bar???";
record.bookmarkURI = "http://bar.foo.com";
record.description = "This is a description for Bar???";
record.tags = tags;
record.keyword = "keywordzzz";
record.parentID = topFolderGuid;
record.parentName = topFolderName;
record.type = "bookmark";
return record;
}
- @SuppressWarnings("unchecked")
public static BookmarkRecord createBookmark3() {
BookmarkRecord record = new BookmarkRecord();
JSONArray tags = new JSONArray();
- tags.add("tag1");
- tags.add("tag2");
+ tags.put("tag1");
+ tags.put("tag2");
record.guid = bmk3Guid;
record.title = "Bmk3";
record.bookmarkURI = "http://bmk3.com";
record.description = "This is a description for bmk3";
record.tags = tags;
record.keyword = "snooozzz";
record.parentID = middleFolderGuid;
record.parentName = middleFolderName;
record.type = "bookmark";
return record;
}
- @SuppressWarnings("unchecked")
public static BookmarkRecord createBookmark4() {
BookmarkRecord record = new BookmarkRecord();
JSONArray tags = new JSONArray();
- tags.add("tag1");
- tags.add("tag2");
+ tags.put("tag1");
+ tags.put("tag2");
record.guid = bmk4Guid;
record.title = "Bmk4";
record.bookmarkURI = "http://bmk4.com";
record.description = "This is a description for bmk4?";
record.tags = tags;
record.keyword = "booooozzz";
record.parentID = bottomFolderGuid;
record.parentName = bottomFolderName;
record.type = "bookmark";
return record;
}
- @SuppressWarnings("unchecked")
public static BookmarkRecord createMicrosummary() {
BookmarkRecord record = new BookmarkRecord();
JSONArray tags = new JSONArray();
- tags.add("tag1");
- tags.add("tag2");
+ tags.put("tag1");
+ tags.put("tag2");
record.guid = Utils.generateGuid();
record.title = "Microsummary 1";
record.bookmarkURI = "www.bmkuri.com";
record.description = "microsummary description";
record.tags = tags;
record.keyword = "keywordzzz";
record.parentID = topFolderGuid;
record.parentName = topFolderName;
@@ -141,69 +136,65 @@ public class BookmarkHelpers {
record.tags = new JSONArray();
record.keyword = "queryKeyword";
record.parentID = topFolderGuid;
record.parentName = topFolderName;
record.type = "query";
return record;
}
- @SuppressWarnings("unchecked")
public static BookmarkRecord createFolder1() {
BookmarkRecord record = new BookmarkRecord();
record.guid = topFolderGuid;
record.title = topFolderName;
record.parentID = "mobile";
record.parentName = "mobile";
JSONArray children = new JSONArray();
- children.add(bmk1Guid);
- children.add(bmk2Guid);
+ children.put(bmk1Guid);
+ children.put(bmk2Guid);
record.children = children;
record.type = "folder";
return record;
}
- @SuppressWarnings("unchecked")
public static BookmarkRecord createFolder2() {
BookmarkRecord record = new BookmarkRecord();
record.guid = middleFolderGuid;
record.title = middleFolderName;
record.parentID = topFolderGuid;
record.parentName = topFolderName;
JSONArray children = new JSONArray();
- children.add(bmk3Guid);
+ children.put(bmk3Guid);
record.children = children;
record.type = "folder";
return record;
}
- @SuppressWarnings("unchecked")
public static BookmarkRecord createFolder3() {
BookmarkRecord record = new BookmarkRecord();
record.guid = bottomFolderGuid;
record.title = bottomFolderName;
record.parentID = middleFolderGuid;
record.parentName = middleFolderName;
JSONArray children = new JSONArray();
- children.add(bmk4Guid);
+ children.put(bmk4Guid);
record.children = children;
record.type = "folder";
return record;
}
- @SuppressWarnings("unchecked")
public static BookmarkRecord createLivemark() {
BookmarkRecord record = new BookmarkRecord();
record.guid = Utils.generateGuid();
record.title = "Livemark title";
record.parentID = topFolderGuid;
record.parentName = topFolderName;
JSONArray children = new JSONArray();
- children.add(Utils.generateGuid());
- children.add(Utils.generateGuid());
+ children.put(Utils.generateGuid());
+ children.put(Utils.generateGuid());
record.children = children;
record.type = "livemark";
return record;
}
public static BookmarkRecord createSeparator() {
BookmarkRecord record = new BookmarkRecord();
record.guid = Utils.generateGuid();
--- a/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/sync/helpers/HistoryHelpers.java
+++ b/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/sync/helpers/HistoryHelpers.java
@@ -1,90 +1,87 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.background.sync.helpers;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.mozilla.gecko.sync.repositories.domain.HistoryRecord;
public class HistoryHelpers {
-
- @SuppressWarnings("unchecked")
- private static JSONArray getVisits1() {
+ private static JSONArray getVisits1() throws JSONException {
JSONArray json = new JSONArray();
JSONObject obj = new JSONObject();
obj.put("date", 1320087601465600000L);
obj.put("type", 2L);
- json.add(obj);
+ json.put(obj);
obj = new JSONObject();
obj.put("date", 1320084970724990000L);
obj.put("type", 1L);
- json.add(obj);
+ json.put(obj);
obj = new JSONObject();
obj.put("date", 1319764134412287000L);
obj.put("type", 1L);
- json.add(obj);
+ json.put(obj);
obj = new JSONObject();
obj.put("date", 1319681306455594000L);
obj.put("type", 2L);
- json.add(obj);
+ json.put(obj);
return json;
}
- @SuppressWarnings("unchecked")
- private static JSONArray getVisits2() {
+ private static JSONArray getVisits2() throws JSONException {
JSONArray json = new JSONArray();
JSONObject obj = new JSONObject();
- obj = new JSONObject();
obj.put("date", 1319764134412345000L);
obj.put("type", 4L);
- json.add(obj);
+ json.put(obj);
obj = new JSONObject();
obj.put("date", 1319681306454321000L);
obj.put("type", 3L);
- json.add(obj);
+ json.put(obj);
return json;
}
- public static HistoryRecord createHistory1() {
+ public static HistoryRecord createHistory1() throws JSONException {
HistoryRecord record = new HistoryRecord();
record.title = "History 1";
record.histURI = "http://history.page1.com";
record.visits = getVisits1();
return record;
}
- public static HistoryRecord createHistory2() {
+ public static HistoryRecord createHistory2() throws JSONException {
HistoryRecord record = new HistoryRecord();
record.title = "History 2";
record.histURI = "http://history.page2.com";
record.visits = getVisits2();
return record;
}
- public static HistoryRecord createHistory3() {
+ public static HistoryRecord createHistory3() throws JSONException {
HistoryRecord record = new HistoryRecord();
record.title = "History 3";
record.histURI = "http://history.page3.com";
record.visits = getVisits2();
return record;
}
- public static HistoryRecord createHistory4() {
+ public static HistoryRecord createHistory4() throws JSONException {
HistoryRecord record = new HistoryRecord();
record.title = "History 4";
record.histURI = "http://history.page4.com";
record.visits = getVisits1();
return record;
}
- public static HistoryRecord createHistory5() {
+ public static HistoryRecord createHistory5() throws JSONException {
HistoryRecord record = new HistoryRecord();
record.title = "History 5";
record.histURI = "http://history.page5.com";
record.visits = getVisits2();
return record;
}
}
\ No newline at end of file
--- a/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/testhelpers/MockRecord.java
+++ b/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/testhelpers/MockRecord.java
@@ -1,14 +1,13 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.background.testhelpers;
-import org.json.simple.JSONObject;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.repositories.domain.Record;
public class MockRecord extends Record {
public MockRecord(String guid, String collection, long lastModified, boolean deleted) {
super(guid, collection, lastModified, deleted);
}
@@ -25,20 +24,20 @@ public class MockRecord extends Record {
public Record copyWithIDs(String guid, long androidID) {
MockRecord r = new MockRecord(guid, this.collection, this.lastModified, this.deleted);
r.androidID = androidID;
return r;
}
@Override
public String toJSONString() {
- return toJSONObject().toJSONString();
+ return toJSONObject().toString();
}
@Override
- public JSONObject toJSONObject() {
+ public ExtendedJSONObject toJSONObject() {
final ExtendedJSONObject o = new ExtendedJSONObject();
o.put("payload", "foo");
o.put("id", guid);
o.put("ttl", 60);
- return o.object;
+ return o;
}
-}
\ No newline at end of file
+}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/db/Tab.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/background/db/Tab.java
@@ -1,15 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.background.db;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
+import org.json.JSONException;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.BrowserContract.Tabs;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.repositories.android.RepoUtils;
import android.content.ContentValues;
import android.database.Cursor;
@@ -30,18 +31,24 @@ public class Tab {
public ContentValues toContentValues(String clientGUID, int position) {
ContentValues out = new ContentValues();
out.put(BrowserContract.Tabs.POSITION, position);
out.put(BrowserContract.Tabs.CLIENT_GUID, clientGUID);
out.put(BrowserContract.Tabs.FAVICON, this.icon);
out.put(BrowserContract.Tabs.LAST_USED, this.lastUsed);
out.put(BrowserContract.Tabs.TITLE, this.title);
- out.put(BrowserContract.Tabs.URL, (String) this.history.get(0));
- out.put(BrowserContract.Tabs.HISTORY, this.history.toJSONString());
+ try {
+ out.put(BrowserContract.Tabs.URL, (String) this.history.get(0));
+ } catch (JSONException e) {
+ // In the spirit of "be strict about what we send out", crash.
+ // Revisit this upstream if crashes actually happen in the wild.
+ throw new IllegalStateException("Tab's history array missing 0th element.");
+ }
+ out.put(BrowserContract.Tabs.HISTORY, this.history.toString());
return out;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Tab)) {
return false;
}
@@ -53,17 +60,17 @@ public class Tab {
if (!RepoUtils.stringsEqual(this.icon, other.icon)) {
return false;
}
if (!(this.lastUsed == other.lastUsed)) {
return false;
}
- return Utils.sameArrays(this.history, other.history);
+ return this.history.equals(other.history);
}
@Override
public int hashCode() {
return super.hashCode();
}
/**
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient20.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient20.java
@@ -1,18 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.background.fxa;
import android.support.annotation.NonNull;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONObject;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientMalformedResponseException;
import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.Locales;
import org.mozilla.gecko.fxa.devices.FxAccountDevice;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.Utils;
@@ -876,17 +876,17 @@ public class FxAccountClient20 implement
invokeHandleError(delegate, e);
return;
}
resource.delegate = new ResourceDelegate<FxAccountDevice[]>(resource, delegate, ResponseType.JSON_ARRAY, tokenId, reqHMACKey) {
@Override
public void handleSuccess(int status, HttpResponse response, JSONArray devicesJson) {
try {
- FxAccountDevice[] devices = new FxAccountDevice[devicesJson.size()];
+ FxAccountDevice[] devices = new FxAccountDevice[devicesJson.length()];
for (int i = 0; i < devices.length; i++) {
ExtendedJSONObject deviceJson = new ExtendedJSONObject((JSONObject) devicesJson.get(i));
devices[i] = FxAccountDevice.fromJson(deviceJson);
}
delegate.handleSuccess(devices);
} catch (Exception e) {
delegate.handleError(e);
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/browserid/JSONWebTokenUtils.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/browserid/JSONWebTokenUtils.java
@@ -1,19 +1,21 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.browserid;
-import org.json.simple.JSONObject;
+import android.support.annotation.VisibleForTesting;
+
+import org.json.JSONException;
+import org.json.JSONObject;
import org.mozilla.apache.commons.codec.binary.Base64;
import org.mozilla.apache.commons.codec.binary.StringUtils;
import org.mozilla.gecko.sync.ExtendedJSONObject;
-import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.Utils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.TreeMap;
@@ -59,51 +61,50 @@ public class JSONWebTokenUtils {
boolean verifies = publicKey.verifyMessage(message, signature);
if (!verifies) {
throw new GeneralSecurityException("bad signature");
}
String payload = StringUtils.newStringUtf8(Base64.decodeBase64(segments[1]));
return payload;
}
- /**
- * Public for testing.
- */
- @SuppressWarnings("unchecked")
+ @VisibleForTesting
public static String getPayloadString(String payloadString, String audience, String issuer,
- Long issuedAt, long expiresAt) throws NonObjectJSONException, IOException {
+ Long issuedAt, long expiresAt) throws JSONException {
ExtendedJSONObject payload;
if (payloadString != null) {
payload = new ExtendedJSONObject(payloadString);
} else {
payload = new ExtendedJSONObject();
}
if (audience != null) {
payload.put("aud", audience);
}
payload.put("iss", issuer);
if (issuedAt != null) {
payload.put("iat", issuedAt);
}
payload.put("exp", expiresAt);
// TreeMap so that keys are sorted. A small attempt to keep output stable over time.
- return JSONObject.toJSONString(new TreeMap<Object, Object>(payload.object));
+ // TODO json output, this is broken.
+ return ExtendedJSONObject.toTreeMap(payload).toString();
+// return JSONObject.toJSONString(ExtendedJSONObject.toTreeMap(payload));
}
- protected static String getCertificatePayloadString(VerifyingPublicKey publicKeyToSign, String email) throws NonObjectJSONException, IOException {
+ private static String getCertificatePayloadString(VerifyingPublicKey publicKeyToSign, String email) {
ExtendedJSONObject payload = new ExtendedJSONObject();
ExtendedJSONObject principal = new ExtendedJSONObject();
principal.put("email", email);
payload.put("principal", principal);
payload.put("public-key", publicKeyToSign.toJSONObject());
return payload.toJSONString();
}
public static String createCertificate(VerifyingPublicKey publicKeyToSign, String email,
- String issuer, long issuedAt, long expiresAt, SigningPrivateKey privateKey) throws NonObjectJSONException, IOException, GeneralSecurityException {
+ String issuer, long issuedAt, long expiresAt, SigningPrivateKey privateKey) throws JSONException, IOException, GeneralSecurityException {
String certificatePayloadString = getCertificatePayloadString(publicKeyToSign, email);
String payloadString = getPayloadString(certificatePayloadString, null, issuer, issuedAt, expiresAt);
return JSONWebTokenUtils.encode(payloadString, privateKey);
}
/**
* Create a Browser ID assertion.
*
@@ -118,22 +119,22 @@ public class JSONWebTokenUtils {
* @param issuer
* to produce assertion for.
* @param issuedAt
* timestamp for assertion, in milliseconds since the epoch; if null,
* no timestamp is included.
* @param expiresAt
* expiration timestamp for assertion, in milliseconds since the epoch.
* @return assertion.
- * @throws NonObjectJSONException
+ * @throws JSONException
* @throws IOException
* @throws GeneralSecurityException
*/
public static String createAssertion(SigningPrivateKey privateKeyToSignWith, String certificate, String audience,
- String issuer, Long issuedAt, long expiresAt) throws NonObjectJSONException, IOException, GeneralSecurityException {
+ String issuer, Long issuedAt, long expiresAt) throws JSONException, IOException, GeneralSecurityException {
String emptyAssertionPayloadString = "{}";
String payloadString = getPayloadString(emptyAssertionPayloadString, audience, issuer, issuedAt, expiresAt);
String signature = JSONWebTokenUtils.encode(payloadString, privateKeyToSignWith);
return certificate + "~" + signature;
}
/**
* For debugging only!
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/authenticator/AccountPickler.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/authenticator/AccountPickler.java
@@ -8,16 +8,17 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
+import org.json.JSONException;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.login.State.StateLabel;
import org.mozilla.gecko.fxa.login.StateFactory;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.NonObjectJSONException;
@@ -157,17 +158,17 @@ public class AccountPickler {
}
} catch (Exception e) {
Logger.warn(LOG_TAG, "Caught exception persisting account settings to " + filename +
"; ignoring.", e);
}
}
/* package-private */ synchronized static UnpickleParams unpickleParams(final Context context, final String filename)
- throws IOException, InvalidKeySpecException, SecurityException, NoSuchAlgorithmException, NonObjectJSONException {
+ throws IOException, InvalidKeySpecException, SecurityException, NoSuchAlgorithmException, NonObjectJSONException, JSONException {
final String jsonString = Utils.readFile(context, filename);
final ExtendedJSONObject json = new ExtendedJSONObject(jsonString);
return UnpickleParams.fromJSON(json);
}
/**
* Create Android account from saved JSON object. Assumes that an account does not exist.
* This operation is synchronized to avoid race condition while deleting the account.
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/authenticator/AndroidFxAccount.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/authenticator/AndroidFxAccount.java
@@ -24,29 +24,29 @@ import android.os.Handler;
import android.os.ResultReceiver;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
+import org.json.JSONException;
import org.mozilla.gecko.background.common.GlobalConstants;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountUtils;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.EnvironmentUtils;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.login.State.StateLabel;
import org.mozilla.gecko.fxa.login.StateFactory;
import org.mozilla.gecko.fxa.sync.FxAccountProfileService;
import org.mozilla.gecko.sync.ExtendedJSONObject;
-import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.ThreadPool;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.setup.Constants;
import org.mozilla.gecko.util.ThreadUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
@@ -1085,17 +1085,17 @@ public class AndroidFxAccount {
}
}
}
private void renameAccountIfNecessary(final String profileData, final Runnable callback) {
final ExtendedJSONObject profileJSON;
try {
profileJSON = new ExtendedJSONObject(profileData);
- } catch (NonObjectJSONException | IOException e) {
+ } catch (JSONException e) {
Logger.error(LOG_TAG, "Error processing fetched account json string", e);
callback.run();
return;
}
if (!profileJSON.containsKey("email")) {
Logger.error(LOG_TAG, "Profile JSON missing email key");
callback.run();
return;
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/login/Married.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/login/Married.java
@@ -8,16 +8,17 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import org.json.JSONException;
import org.mozilla.gecko.background.fxa.FxAccountUtils;
import org.mozilla.gecko.browserid.BrowserIDKeyPair;
import org.mozilla.gecko.browserid.JSONWebTokenUtils;
import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine.ExecuteDelegate;
import org.mozilla.gecko.fxa.login.FxAccountLoginTransition.LogMessage;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.Utils;
@@ -42,17 +43,17 @@ public class Married extends TokensAndKe
return o;
}
@Override
public void execute(final ExecuteDelegate delegate) {
delegate.handleTransition(new LogMessage("staying married"), this);
}
- public String generateAssertion(String audience, String issuer) throws NonObjectJSONException, IOException, GeneralSecurityException {
+ public String generateAssertion(String audience, String issuer) throws JSONException, IOException, GeneralSecurityException {
// We generate assertions with no iat and an exp after 2050 to avoid
// invalid-timestamp errors from the token server.
final long expiresAt = JSONWebTokenUtils.DEFAULT_FUTURE_EXPIRES_AT_IN_MILLISECONDS;
String assertion = JSONWebTokenUtils.createAssertion(keyPair.getPrivate(), certificate, audience, issuer, null, expiresAt);
if (!FxAccountUtils.LOG_PERSONAL_INFORMATION) {
return assertion;
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/CollectionKeys.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/CollectionKeys.java
@@ -6,17 +6,18 @@ package org.mozilla.gecko.sync;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
+import org.json.JSONException;
import org.mozilla.apache.commons.codec.binary.Base64;
import org.mozilla.gecko.sync.crypto.CryptoException;
import org.mozilla.gecko.sync.crypto.KeyBundle;
import org.mozilla.gecko.util.StringUtils;
public class CollectionKeys {
private KeyBundle defaultKeyBundle = null;
private final HashMap<String, KeyBundle> collectionKeyBundles = new HashMap<String, KeyBundle>();
@@ -55,28 +56,28 @@ public class CollectionKeys {
}
return this.defaultKeyBundle;
}
/**
* Take a pair of values in a JSON array, handing them off to KeyBundle to
* produce a usable keypair.
*/
- private static KeyBundle arrayToKeyBundle(JSONArray array) throws UnsupportedEncodingException {
+ private static KeyBundle arrayToKeyBundle(JSONArray array) throws UnsupportedEncodingException, JSONException {
String encKeyStr = (String) array.get(0);
String hmacKeyStr = (String) array.get(1);
return KeyBundle.fromBase64EncodedKeys(encKeyStr, hmacKeyStr);
}
@SuppressWarnings("unchecked")
private static JSONArray keyBundleToArray(KeyBundle bundle) {
// Generate JSON.
JSONArray keysArray = new JSONArray();
- keysArray.add(new String(Base64.encodeBase64(bundle.getEncryptionKey()), StringUtils.UTF_8));
- keysArray.add(new String(Base64.encodeBase64(bundle.getHMACKey()), StringUtils.UTF_8));
+ keysArray.put(new String(Base64.encodeBase64(bundle.getEncryptionKey()), StringUtils.UTF_8));
+ keysArray.put(new String(Base64.encodeBase64(bundle.getHMACKey()), StringUtils.UTF_8));
return keysArray;
}
private ExtendedJSONObject asRecordContents() throws NoCollectionKeysSetException {
ExtendedJSONObject json = new ExtendedJSONObject();
json.put("id", "keys");
json.put("collection", "crypto");
json.put("default", keyBundleToArray(this.defaultKeyBundle()));
@@ -103,32 +104,32 @@ public class CollectionKeys {
*
* @param keys
* A "crypto/keys" <code>CryptoRecord</code>, encrypted with
* <code>syncKeyBundle</code> if <code>syncKeyBundle</code> is non-null.
* @param syncKeyBundle
* If non-null, the sync key bundle to decrypt <code>keys</code> with.
*/
public void setKeyPairsFromWBO(CryptoRecord keys, KeyBundle syncKeyBundle)
- throws CryptoException, IOException, NonObjectJSONException {
+ throws CryptoException, IOException, NonObjectJSONException, JSONException {
if (keys == null) {
throw new IllegalArgumentException("cannot set key pairs from null record");
}
if (syncKeyBundle != null) {
keys.keyBundle = syncKeyBundle;
keys.decrypt();
}
ExtendedJSONObject cleartext = keys.payload;
KeyBundle defaultKey = arrayToKeyBundle((JSONArray) cleartext.get("default"));
ExtendedJSONObject collections = cleartext.getObject("collections");
HashMap<String, KeyBundle> collectionKeys = new HashMap<String, KeyBundle>();
- for (Entry<String, Object> pair : collections.entrySet()) {
- KeyBundle bundle = arrayToKeyBundle((JSONArray) pair.getValue());
- collectionKeys.put(pair.getKey(), bundle);
+ for (String key : collections.keySet()) {
+ KeyBundle bundle = arrayToKeyBundle((JSONArray) collections.get(key));
+ collectionKeys.put(key, bundle);
}
this.collectionKeyBundles.clear();
this.collectionKeyBundles.putAll(collectionKeys);
this.defaultKeyBundle = defaultKey;
}
public void setKeyBundleForCollection(String collection, KeyBundle keys) {
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/CommandProcessor.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/CommandProcessor.java
@@ -7,18 +7,19 @@ package org.mozilla.gecko.sync;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.sync.repositories.NullCursorException;
import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
import org.mozilla.gecko.sync.telemetry.TelemetryEventCollector;
@@ -74,38 +75,41 @@ public class CommandProcessor {
/**
* Get list of arguments as strings. Individual arguments may be null.
*
* @return list of strings.
*/
public synchronized List<String> getArgsList() {
if (argsList == null) {
- ArrayList<String> argsList = new ArrayList<String>(args.size());
+ ArrayList<String> argsList = new ArrayList<String>(args.length());
- for (int i = 0; i < args.size(); i++) {
- final Object arg = args.get(i);
+ for (int i = 0; i < args.length(); i++) {
+ final Object arg = Utils.getOrThrow(args, i);
if (arg == null) {
argsList.add(null);
continue;
}
argsList.add(arg.toString());
}
this.argsList = argsList;
}
return this.argsList;
}
- @SuppressWarnings("unchecked")
public JSONObject asJSONObject() {
JSONObject out = new JSONObject();
- out.put("command", this.commandType);
- out.put("args", this.args);
- if (this.flowID != null) {
- out.put("flowID", this.flowID);
+ try {
+ out.put("command", this.commandType);
+ out.put("args", this.args);
+ if (this.flowID != null) {
+ out.put("flowID", this.flowID);
+ }
+ } catch (JSONException e) {
+ throw new IllegalStateException("'command' or 'args' is null?", e);
}
return out;
}
}
/**
* Register a command.
* <p>
@@ -160,17 +164,17 @@ public class CommandProcessor {
/**
* Parse a JSON command into a ParsedCommand object for easier handling.
*
* @param unparsedCommand - command as ExtendedJSONObject
* @return - null if command is invalid, else return ParsedCommand with
* no null attributes.
*/
protected static Command parseCommand(ExtendedJSONObject unparsedCommand) {
- String type = (String) unparsedCommand.get("command");
+ String type = unparsedCommand.getString("command");
if (type == null) {
return null;
}
try {
JSONArray unparsedArgs = unparsedCommand.getArray("args");
if (unparsedArgs == null) {
return null;
@@ -187,19 +191,19 @@ public class CommandProcessor {
@SuppressWarnings("unchecked")
public void sendURIToClientForDisplay(String uri, String clientID, String title, String sender, Context context) {
Logger.info(LOG_TAG, "Sending URI to client " + clientID + ".");
if (Logger.LOG_PERSONAL_INFORMATION) {
Logger.pii(LOG_TAG, "URI is " + uri + "; title is '" + title + "'.");
}
final JSONArray args = new JSONArray();
- args.add(uri);
- args.add(sender);
- args.add(title);
+ args.put(uri);
+ args.put(sender);
+ args.put(title);
final String flowID = Utils.generateGuid();
final Command displayURICommand = new Command("displayURI", args, flowID);
this.sendCommand(clientID, displayURICommand, context);
}
/**
* Validates and sends a command to a client or all clients.
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/CryptoRecord.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/CryptoRecord.java
@@ -2,17 +2,18 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
-import org.json.simple.JSONObject;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.mozilla.apache.commons.codec.binary.Base64;
import org.mozilla.gecko.sync.crypto.CryptoException;
import org.mozilla.gecko.sync.crypto.CryptoInfo;
import org.mozilla.gecko.sync.crypto.KeyBundle;
import org.mozilla.gecko.sync.crypto.MissingCryptoInputException;
import org.mozilla.gecko.sync.crypto.NoKeyBundleException;
import org.mozilla.gecko.sync.repositories.domain.Record;
import org.mozilla.gecko.sync.repositories.domain.RecordParseException;
@@ -54,19 +55,19 @@ public class CryptoRecord extends Record
* Helper method for doing actual decryption.
*
* Input: JSONObject containing a valid payload (cipherText, IV, HMAC),
* KeyBundle with keys for decryption. Output: byte[] clearText
* @throws CryptoException
* @throws UnsupportedEncodingException
*/
private static byte[] decryptPayload(ExtendedJSONObject payload, KeyBundle keybundle) throws CryptoException, UnsupportedEncodingException {
- byte[] ciphertext = Base64.decodeBase64(((String) payload.get(KEY_CIPHERTEXT)).getBytes("UTF-8"));
- byte[] iv = Base64.decodeBase64(((String) payload.get(KEY_IV)).getBytes("UTF-8"));
- byte[] hmac = Utils.hex2Byte((String) payload.get(KEY_HMAC));
+ byte[] ciphertext = Base64.decodeBase64((payload.getString(KEY_CIPHERTEXT)).getBytes("UTF-8"));
+ byte[] iv = Base64.decodeBase64((payload.getString(KEY_IV)).getBytes("UTF-8"));
+ byte[] hmac = Utils.hex2Byte(payload.getString(KEY_HMAC));
return CryptoInfo.decrypt(ciphertext, iv, hmac, keybundle).getMessage();
}
// The encrypted JSON body object.
// The decrypted JSON body object. Fields are copied from `body`.
public ExtendedJSONObject payload;
@@ -83,17 +84,17 @@ public class CryptoRecord extends Record
super(null, null, 0, false);
if (payload == null) {
throw new IllegalArgumentException(
"No payload provided to CryptoRecord constructor.");
}
this.payload = payload;
}
- public CryptoRecord(String jsonString) throws IOException, NonObjectJSONException {
+ public CryptoRecord(String jsonString) throws IOException, JSONException {
this(new ExtendedJSONObject(jsonString));
}
/**
* Create a new CryptoRecord with the same metadata as an existing record.
*
* @param source
@@ -125,29 +126,29 @@ public class CryptoRecord extends Record
* @param jsonRecord
* @return
* A CryptoRecord that encapsulates the provided record.
*
* @throws NonObjectJSONException
* @throws IOException
*/
public static CryptoRecord fromJSONRecord(String jsonRecord)
- throws NonObjectJSONException, IOException, RecordParseException {
+ throws JSONException, IOException, RecordParseException {
byte[] bytes = jsonRecord.getBytes("UTF-8");
ExtendedJSONObject object = ExtendedJSONObject.parseUTF8AsJSONObject(bytes);
return CryptoRecord.fromJSONRecord(object);
}
// TODO: defensive programming.
public static CryptoRecord fromJSONRecord(ExtendedJSONObject jsonRecord)
- throws IOException, NonObjectJSONException, RecordParseException {
- String id = (String) jsonRecord.get(KEY_ID);
- String collection = (String) jsonRecord.get(KEY_COLLECTION);
- String jsonEncodedPayload = (String) jsonRecord.get(KEY_PAYLOAD);
+ throws IOException, JSONException, RecordParseException {
+ String id = jsonRecord.getString(KEY_ID);
+ String collection = jsonRecord.getString(KEY_COLLECTION);
+ String jsonEncodedPayload = jsonRecord.getString(KEY_PAYLOAD);
ExtendedJSONObject payload = new ExtendedJSONObject(jsonEncodedPayload);
CryptoRecord record = new CryptoRecord(payload);
record.guid = id;
record.collection = collection;
if (jsonRecord.containsKey(KEY_MODIFIED)) {
Long timestamp = jsonRecord.getTimestamp(KEY_MODIFIED);
@@ -177,17 +178,17 @@ public class CryptoRecord extends Record
// TODO: deleted?
return record;
}
public void setKeyBundle(KeyBundle bundle) {
this.keyBundle = bundle;
}
- public CryptoRecord decrypt() throws CryptoException, IOException, NonObjectJSONException {
+ public CryptoRecord decrypt() throws CryptoException, IOException, JSONException {
if (keyBundle == null) {
throw new NoKeyBundleException();
}
// Check that payload contains all pieces for crypto.
if (!payload.containsKey(KEY_CIPHERTEXT) ||
!payload.containsKey(KEY_IV) ||
!payload.containsKey(KEY_HMAC)) {
@@ -236,23 +237,23 @@ public class CryptoRecord extends Record
@Override
protected void initFromPayload(ExtendedJSONObject payload) {
throw new IllegalStateException("Can't do this with a CryptoRecord.");
}
// TODO: this only works with encrypted object, and has other limitations.
@Override
- public JSONObject toJSONObject() {
+ public ExtendedJSONObject toJSONObject() {
ExtendedJSONObject o = new ExtendedJSONObject();
o.put(KEY_PAYLOAD, payload.toJSONString());
o.put(KEY_ID, this.guid);
if (this.ttl > 0) {
o.put(KEY_TTL, this.ttl);
}
- return o.object;
+ return o;
}
@Override
public String toJSONString() {
- return toJSONObject().toJSONString();
+ return toJSONObject().toString();
}
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/ExtendedJSONObject.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/ExtendedJSONObject.java
@@ -1,207 +1,135 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
-import org.json.simple.parser.JSONParser;
-import org.json.simple.parser.ParseException;
-import org.mozilla.apache.commons.codec.binary.Base64;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.mozilla.gecko.sync.UnexpectedJSONException.BadRequiredFieldJSONException;
import java.io.IOException;
import java.io.Reader;
-import java.io.StringReader;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
+import java.util.Stack;
+import java.util.TreeMap;
/**
* Extend JSONObject to do little things, like, y'know, accessing members.
*
* @author rnewman
*
*/
public class ExtendedJSONObject implements Cloneable {
public JSONObject object;
/**
- * Return a <code>JSONParser</code> instance for immediate use.
- * <p>
- * <code>JSONParser</code> is not thread-safe, so we return a new instance
- * each call. This is extremely inefficient in execution time and especially
- * memory use -- each instance allocates a 16kb temporary buffer -- and we
- * hope to improve matters eventually.
- */
- protected static JSONParser getJSONParser() {
- return new JSONParser();
- }
-
- /**
- * Parse a JSON encoded string.
- *
- * @param in <code>Reader</code> over a JSON-encoded input to parse; not
- * necessarily a JSON object.
- * @return a regular Java <code>Object</code>.
- * @throws ParseException
- * @throws IOException
- */
- protected static Object parseRaw(Reader in) throws ParseException, IOException {
- try {
- return getJSONParser().parse(in);
- } catch (Error e) {
- // Don't be stupid, org.json.simple. Bug 1042929.
- throw new ParseException(ParseException.ERROR_UNEXPECTED_EXCEPTION, e);
- }
- }
-
- /**
- * Parse a JSON encoded string.
- * <p>
- * You should prefer the streaming interface {@link #parseRaw(Reader)}.
- *
- * @param input JSON-encoded input string to parse; not necessarily a JSON object.
- * @return a regular Java <code>Object</code>.
- * @throws ParseException
- */
- protected static Object parseRaw(String input) throws ParseException {
- try {
- return getJSONParser().parse(input);
- } catch (Error e) {
- // Don't be stupid, org.json.simple. Bug 1042929.
- throw new ParseException(ParseException.ERROR_UNEXPECTED_EXCEPTION, e);
- }
- }
-
- /**
- * Helper method to get a JSON array from a stream.
- *
- * @param in <code>Reader</code> over a JSON-encoded array to parse.
- * @throws ParseException
- * @throws IOException
- * @throws NonArrayJSONException if the object is valid JSON, but not an array.
- */
- public static JSONArray parseJSONArray(Reader in)
- throws IOException, ParseException, NonArrayJSONException {
- Object o = parseRaw(in);
-
- if (o == null) {
- return null;
- }
-
- if (o instanceof JSONArray) {
- return (JSONArray) o;
- }
-
- throw new NonArrayJSONException("value must be a JSON array");
- }
-
- /**
* Helper method to get a JSON array from a string.
* <p>
- * You should prefer the stream interface {@link #parseJSONArray(Reader)}.
*
* @param jsonString input.
- * @throws IOException
- * @throws NonArrayJSONException if the object is invalid JSON or not an array.
+ * @throws JSONException if the object is invalid JSON or not an array.
*/
- public static JSONArray parseJSONArray(String jsonString)
- throws IOException, NonArrayJSONException {
- Object o = null;
- try {
- o = parseRaw(jsonString);
- } catch (ParseException e) {
- throw new NonArrayJSONException(e);
- }
-
- if (o == null) {
- return null;
- }
-
- if (o instanceof JSONArray) {
- return (JSONArray) o;
- }
-
- throw new NonArrayJSONException("value must be a JSON array");
+ public static JSONArray parseJSONArray(String jsonString) throws JSONException {
+ return new JSONArray(jsonString);
}
/**
* Helper method to get a JSON object from a UTF-8 byte array.
*
* @param in UTF-8 bytes.
- * @throws NonObjectJSONException if the object is not valid JSON or not an object.
+ * @throws JSONException if the object is not valid JSON or not an object.
* @throws IOException
*/
public static ExtendedJSONObject parseUTF8AsJSONObject(byte[] in)
- throws NonObjectJSONException, IOException {
+ throws JSONException, IOException {
return new ExtendedJSONObject(new String(in, "UTF-8"));
}
public ExtendedJSONObject() {
this.object = new JSONObject();
}
public ExtendedJSONObject(JSONObject o) {
this.object = o;
}
- public ExtendedJSONObject(Reader in) throws IOException, NonObjectJSONException {
- if (in == null) {
- this.object = new JSONObject();
- return;
- }
-
- Object obj = null;
- try {
- obj = parseRaw(in);
- } catch (ParseException e) {
- throw new NonObjectJSONException(e);
- }
-
- if (obj instanceof JSONObject) {
- this.object = ((JSONObject) obj);
- } else {
- throw new NonObjectJSONException("value must be a JSON object");
- }
+ public ExtendedJSONObject(Reader in) throws IOException, JSONException {
+ final String inputString = Utils.readerToString(in);
+ this.object = new JSONObject(inputString);
}
- public ExtendedJSONObject(String jsonString) throws IOException, NonObjectJSONException {
- this(jsonString == null ? null : new StringReader(jsonString));
+ public ExtendedJSONObject(String jsonString) throws JSONException {
+ // Compatibility with the old implementation.
+ final JSONObject obj = jsonString != null ? new JSONObject(jsonString) : new JSONObject();
+ this.object = obj;
}
@Override
public ExtendedJSONObject clone() throws CloneNotSupportedException {
- return new ExtendedJSONObject((JSONObject) this.object.clone());
+ super.clone();
+ final JSONObject shallowCopy = new JSONObject();
+
+ final Iterator<String> iterator = this.object.keys();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ JSONObject value = this.object.optJSONObject(key);
+
+ try {
+ shallowCopy.put(key, value);
+ } catch (JSONException e) {
+ throw new IllegalStateException("Couldn't clone JSONObject", e);
+ }
+ }
+
+ return new ExtendedJSONObject(shallowCopy);
}
// Passthrough methods.
+ @Nullable
public Object get(String key) {
- return this.object.get(key);
+ try {
+ Object o = this.object.get(key);
+ return o != JSONObject.NULL ? o : null;
+ } catch (JSONException e) {
+ return null;
+ }
}
public long getLong(String key, long def) {
- if (!object.containsKey(key)) {
+ if (!object.has(key)) {
return def;
}
Long val = getLong(key);
if (val == null) {
return def;
}
- return val.longValue();
+ return val;
}
public Long getLong(String key) {
- return (Long) this.get(key);
+ Object val = this.get(key);
+ if (val instanceof Number) {
+ return ((Number) val).longValue();
+ }
+ if (val instanceof String) {
+ return Long.parseLong((String) val, 10);
+ }
+ return null;
}
public String getString(String key) {
return (String) this.get(key);
}
public Boolean getBoolean(String key) {
return (Boolean) this.get(key);
@@ -209,19 +137,22 @@ public class ExtendedJSONObject implemen
/**
* Return an Integer if the value for this key is an Integer, Long, or String
* that can be parsed as a base 10 Integer.
* Passes through null.
*
* @throws NumberFormatException
*/
+ @Nullable
public Integer getIntegerSafely(String key) throws NumberFormatException {
- Object val = this.object.get(key);
- if (val == null) {
+ final Object val;
+ try {
+ val = this.object.get(key);
+ } catch (JSONException e) {
return null;
}
if (val instanceof Integer) {
return (Integer) val;
}
if (val instanceof Long) {
return ((Long) val).intValue();
}
@@ -232,18 +163,25 @@ public class ExtendedJSONObject implemen
}
/**
* Return a server timestamp value as milliseconds since epoch.
*
* @param key
* @return A Long, or null if the value is non-numeric or doesn't exist.
*/
+ @Nullable
public Long getTimestamp(String key) {
- Object val = this.object.get(key);
+ final Object val;
+
+ try {
+ val = this.object.get(key);
+ } catch (JSONException e) {
+ return null;
+ }
// This is absurd.
if (val instanceof Double) {
double millis = ((Double) val) * 1000;
return Double.valueOf(millis).longValue();
}
if (val instanceof Float) {
double millis = ((Float) val).doubleValue() * 1000;
@@ -253,174 +191,233 @@ public class ExtendedJSONObject implemen
// Must be an integral number.
return ((Number) val).longValue() * 1000;
}
return null;
}
public boolean containsKey(String key) {
- return this.object.containsKey(key);
+ return this.object.has(key);
}
public String toJSONString() {
- return this.object.toJSONString();
+ return this.object.toString();
}
@Override
public String toString() {
return this.object.toString();
}
- protected void putRaw(String key, Object value) {
- @SuppressWarnings("unchecked")
- Map<Object, Object> map = this.object;
- map.put(key, value);
+ private void throwingPut(String key, Object value) {
+ try {
+ this.object.put(key, value);
+ } catch (JSONException e) {
+ throw new IllegalArgumentException("Failed to put key/value into JSONObject", e);
+ }
}
public void put(String key, String value) {
- this.putRaw(key, value);
+ this.throwingPut(key, value);
}
public void put(String key, boolean value) {
- this.putRaw(key, value);
+ this.throwingPut(key, value);
}
public void put(String key, long value) {
- this.putRaw(key, value);
+ this.throwingPut(key, value);
}
public void put(String key, int value) {
- this.putRaw(key, value);
+ this.throwingPut(key, value);
}
public void put(String key, ExtendedJSONObject value) {
- this.putRaw(key, value);
+ this.throwingPut(key, value != null ? value.object : null);
}
public void put(String key, JSONArray value) {
- this.putRaw(key, value);
+ this.throwingPut(key, value);
}
@SuppressWarnings("unchecked")
public void putArray(String key, List<String> value) {
// Frustratingly inefficient, but there you have it.
- final JSONArray jsonArray = new JSONArray();
- jsonArray.addAll(value);
- this.putRaw(key, jsonArray);
+ this.put(key, new JSONArray(value));
}
/**
* Remove key-value pair from JSONObject.
*
* @param key
* to be removed.
* @return true if key exists and was removed, false otherwise.
*/
public boolean remove(String key) {
Object res = this.object.remove(key);
return (res != null);
}
+ @Nullable
public ExtendedJSONObject getObject(String key) throws NonObjectJSONException {
- Object o = this.object.get(key);
- if (o == null) {
+ final Object o;
+ try {
+ o = this.object.get(key);
+ } catch (JSONException e) {
return null;
}
if (o instanceof ExtendedJSONObject) {
return (ExtendedJSONObject) o;
}
if (o instanceof JSONObject) {
return new ExtendedJSONObject((JSONObject) o);
}
throw new NonObjectJSONException("value must be a JSON object for key: " + key);
}
- @SuppressWarnings("unchecked")
- public Set<Entry<String, Object>> entrySet() {
- return this.object.entrySet();
+ public Set<String> keySet() {
+ final HashSet<String> keys = new HashSet<>(this.object.length());
+ final Iterator<String> keyIterator = this.object.keys();
+ while (keyIterator.hasNext()) {
+ keys.add(keyIterator.next());
+ }
+ return keys;
}
- @SuppressWarnings("unchecked")
- public Set<String> keySet() {
- return this.object.keySet();
- }
-
- public org.json.simple.JSONArray getArray(String key) throws NonArrayJSONException {
- Object o = this.object.get(key);
- if (o == null) {
+ @Nullable
+ public JSONArray getArray(String key) throws NonArrayJSONException {
+ final Object o;
+ try {
+ o = this.object.get(key);
+ } catch (JSONException e) {
return null;
}
if (o instanceof JSONArray) {
return (JSONArray) o;
}
throw new NonArrayJSONException("key must be a JSON array: " + key);
}
public int size() {
- return this.object.size();
- }
-
- @Override
- public int hashCode() {
- if (this.object == null) {
- return getClass().hashCode();
- }
- return this.object.hashCode() ^ getClass().hashCode();
+ return this.object.length();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof ExtendedJSONObject)) {
return false;
}
if (o == this) {
return true;
}
- ExtendedJSONObject other = (ExtendedJSONObject) o;
- if (this.object == null) {
- return other.object == null;
+ final ExtendedJSONObject other = (ExtendedJSONObject) o;
+ if (this.object == null || other.object == null) {
+ return this.object == other.object;
+ }
+
+ return equalsJSONObject(this.object, ((ExtendedJSONObject) o).object);
+ }
+
+ private static class JSONObjectPair {
+ public JSONObject left;
+ public JSONObject right;
+
+ public JSONObjectPair(JSONObject left, JSONObject right) {
+ this.left = left;
+ this.right = right;
}
- return this.object.equals(other.object);
+ }
+
+ @VisibleForTesting
+ public static boolean equalsJSONObject(JSONObject leftRoot, JSONObject rightRoot) {
+ Stack<JSONObjectPair> stack = new Stack<>();
+ stack.add(new JSONObjectPair(leftRoot, rightRoot));
+ while(stack.size() > 0) {
+ final JSONObjectPair pair = stack.pop();
+ final JSONObject left = pair.left;
+ final JSONObject right = pair.right;
+ if (left.length() != right.length()) {
+ return false;
+ }
+ final Iterator<String> leftKeys = pair.left.keys();
+ while (leftKeys.hasNext()) {
+ final String keyLeft = leftKeys.next();
+
+ if (!right.has(keyLeft)) {
+ return false;
+ }
+
+ final Object valLeft;
+ final Object valRight;
+ try {
+ valLeft = left.get(keyLeft);
+ valRight = right.get(keyLeft);
+ } catch (JSONException e) {
+ return false;
+ }
+
+ if (valLeft instanceof JSONObject && valRight instanceof JSONObject) {
+ stack.add(new JSONObjectPair((JSONObject) valLeft, (JSONObject) valRight));
+ continue;
+ }
+ if (!valLeft.equals(valRight)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ Stack<JSONObject> stack = new Stack<>();
+ stack.add(this.object);
+ while (stack.size() > 0) {
+ final JSONObject object = stack.pop();
+ final Iterator<String> keys = object.keys();
+ while (keys.hasNext()) {
+ final String key = keys.next();
+ final Object val = get(key);
+ if (val == null) {
+ continue;
+ }
+ if (val instanceof JSONObject) {
+ stack.push((JSONObject)val);
+ continue;
+ }
+ result += val.hashCode();
+ }
+ }
+ return result ^ getClass().hashCode();
}
/**
* Throw if keys are missing or values have wrong types.
*
* @param requiredFields list of required keys.
* @param requiredFieldClass class that values must be coercable to; may be null, which means don't check.
- * @throws UnexpectedJSONException
*/
public void throwIfFieldsMissingOrMisTyped(String[] requiredFields, Class<?> requiredFieldClass) throws BadRequiredFieldJSONException {
// Defensive as possible: verify object has expected key(s) with string value.
for (String k : requiredFields) {
Object value = get(k);
if (value == null) {
throw new BadRequiredFieldJSONException("Expected key not present in result: " + k);
}
if (requiredFieldClass != null && !(requiredFieldClass.isInstance(value))) {
throw new BadRequiredFieldJSONException("Value for key not an instance of " + requiredFieldClass + ": " + k);
}
}
}
- /**
- * Return a base64-encoded string value as a byte array.
- */
- public byte[] getByteArrayBase64(String key) {
- String s = (String) this.object.get(key);
- if (s == null) {
- return null;
+ // TODO this is rather expensive. Reevaluate this.
+ // TODO does TreeMap sort while growing?
+ public static TreeMap<Object, Object> toTreeMap(ExtendedJSONObject eoj) {
+ final TreeMap<Object, Object> out = new TreeMap<>();
+ for (String key : eoj.keySet()) {
+ out.put(key, eoj.get(key));
}
- return Base64.decodeBase64(s);
- }
-
- /**
- * Return a hex-encoded string value as a byte array.
- */
- public byte[] getByteArrayHex(String key) {
- String s = (String) this.object.get(key);
- if (s == null) {
- return null;
- }
- return Utils.hex2Byte(s);
+ return out;
}
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/GlobalSession.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/GlobalSession.java
@@ -3,17 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync;
import android.content.Context;
import android.os.SystemClock;
import android.support.annotation.VisibleForTesting;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.crypto.CryptoException;
import org.mozilla.gecko.sync.crypto.KeyBundle;
import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
import org.mozilla.gecko.sync.delegates.FreshStartDelegate;
import org.mozilla.gecko.sync.delegates.GlobalSessionCallback;
import org.mozilla.gecko.sync.delegates.JSONRecordFetchDelegate;
import org.mozilla.gecko.sync.delegates.KeyUploadDelegate;
@@ -719,17 +719,17 @@ public class GlobalSession implements Ht
}
}
// Persist declined.
// Our declined engines at any point are:
// Whatever they were remotely, plus whatever they were locally, less any
// engines that were just enabled locally or remotely.
// If remote just 'won', our recently enabled list just got cleared.
- final HashSet<String> allDeclined = new HashSet<String>();
+ final HashSet<String> allDeclined = new HashSet<>();
final Set<String> newRemoteDeclined = global.getDeclinedEngineNames();
final Set<String> oldLocalDeclined = config.declinedEngineNames;
allDeclined.addAll(newRemoteDeclined);
allDeclined.addAll(oldLocalDeclined);
if (config.userSelectedEngines != null) {
@@ -1019,32 +1019,26 @@ public class GlobalSession implements Ht
public void resetStagesByName(Collection<String> names) {
resetStages(this.getSyncStagesByName(names));
}
/**
* Engines to explicitly mark as declined in a fresh meta/global record.
* <p>
- * Returns an empty array if the user hasn't elected to customize data types,
- * or an array of engines that the user un-checked during customization.
+ * Returns an empty set if the user hasn't elected to customize data types,
+ * or a set of engines that the user un-checked during customization.
* <p>
* Engines that Android Sync doesn't recognize are <b>not</b> included in
- * the returned array.
+ * the returned set.
*
- * @return a new JSONArray of engine names.
+ * @return a new Set of engine names.
*/
- @SuppressWarnings("unchecked")
- protected JSONArray declinedEngineNames() {
- final JSONArray declined = new JSONArray();
- for (String engine : config.declinedEngineNames) {
- declined.add(engine);
- };
-
- return declined;
+ private HashSet<String> declinedEngineNames() {
+ return config.declinedEngineNames;
}
/**
* Engines to include in a fresh meta/global record.
* <p>
* Returns either the persisted engine names (perhaps we have been node
* re-assigned and are initializing a clean server: we want to upload the
* persisted engine names so that we don't accidentally disable engines that
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/InfoCollections.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/InfoCollections.java
@@ -32,19 +32,18 @@ public class InfoCollections {
public InfoCollections() {
this(new ExtendedJSONObject());
}
public InfoCollections(final ExtendedJSONObject record) {
Logger.debug(LOG_TAG, "info/collections is " + record.toJSONString());
HashMap<String, Long> map = new HashMap<String, Long>();
- for (Entry<String, Object> entry : record.entrySet()) {
- final String key = entry.getKey();
- final Object value = entry.getValue();
+ for (String key : record.keySet()) {
+ final Object value = record.get(key);
// These objects are most likely going to be Doubles. Regardless, we
// want to get them in a more sane time format.
if (value instanceof Double) {
map.put(key, Utils.decimalSecondsToMilliseconds((Double) value));
continue;
}
if (value instanceof Long) {
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/InfoCounts.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/InfoCounts.java
@@ -20,24 +20,18 @@ public class InfoCounts {
*/
private Map<String, Integer> counts = null;
@SuppressWarnings("unchecked")
public InfoCounts(final ExtendedJSONObject record) {
Logger.debug(LOG_TAG, "info/collection_counts is " + record.toJSONString());
HashMap<String, Integer> map = new HashMap<String, Integer>();
- Set<Entry<String, Object>> entrySet = record.object.entrySet();
-
- String key;
- Object value;
-
- for (Entry<String, Object> entry : entrySet) {
- key = entry.getKey();
- value = entry.getValue();
+ for (String key : record.keySet()) {
+ final Object value = record.get(key);
if (value instanceof Integer) {
map.put(key, (Integer) value);
continue;
}
if (value instanceof Long) {
map.put(key, ((Long) value).intValue());
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/MetaGlobal.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/MetaGlobal.java
@@ -1,44 +1,46 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync;
+import android.support.annotation.NonNull;
+
import java.io.IOException;
import java.net.URISyntaxException;
-import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
+import org.json.JSONException;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.MetaGlobalException.MetaGlobalMalformedSyncIDException;
import org.mozilla.gecko.sync.MetaGlobalException.MetaGlobalMalformedVersionException;
import org.mozilla.gecko.sync.delegates.MetaGlobalDelegate;
import org.mozilla.gecko.sync.net.AuthHeaderProvider;
import org.mozilla.gecko.sync.net.SyncStorageRecordRequest;
import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate;
import org.mozilla.gecko.sync.net.SyncStorageResponse;
public class MetaGlobal {
private static final String LOG_TAG = "MetaGlobal";
- protected String metaURL;
+ private String metaURL;
// Fields.
- protected ExtendedJSONObject engines;
- protected JSONArray declined;
- protected Long storageVersion;
- protected String syncID;
+ protected ExtendedJSONObject engines;
+ protected HashSet<String> declined = new HashSet<>();
+ private Long storageVersion;
+ protected String syncID;
// Lookup tables.
- protected Map<String, String> syncIDs;
+ private Map<String, String> syncIDs;
protected Map<String, Integer> versions;
protected Map<String, MetaGlobalException> exceptions;
// Temporary location to store our callback.
private MetaGlobalDelegate callback;
// A little hack so we can use the same delegate implementation for upload and download.
private boolean isUploading;
@@ -68,22 +70,22 @@ public class MetaGlobal {
r.delegate = new MetaUploadDelegate(this, lastModifiedTimestamp);
this.callback = callback;
r.put(this.asCryptoRecord());
} catch (Exception e) {
callback.handleError(e);
}
}
- protected ExtendedJSONObject asRecordContents() {
+ private ExtendedJSONObject asRecordContents() {
ExtendedJSONObject json = new ExtendedJSONObject();
json.put("storageVersion", storageVersion);
json.put("engines", engines);
json.put("syncID", syncID);
- json.put("declined", declined);
+ json.put("declined", new JSONArray(declined));
return json;
}
/**
* Return a copy ready for upload.
* @return an unencrypted <code>CryptoRecord</code>.
*/
public CryptoRecord asCryptoRecord() {
@@ -95,99 +97,65 @@ public class MetaGlobal {
return record;
}
public void setFromRecord(CryptoRecord record) throws IllegalStateException, IOException, NonObjectJSONException, NonArrayJSONException {
if (record == null) {
throw new IllegalArgumentException("Cannot set meta/global from null record");
}
Logger.debug(LOG_TAG, "meta/global is " + record.payload.toJSONString());
- this.storageVersion = (Long) record.payload.get("storageVersion");
- this.syncID = (String) record.payload.get("syncID");
+ this.storageVersion = record.payload.getLong("storageVersion");
+ this.syncID = record.payload.getString("syncID");
setEngines(record.payload.getObject("engines"));
// Accepts null -- declined can be missing.
- setDeclinedEngineNames(record.payload.getArray("declined"));
+ final JSONArray declined = record.payload.getArray("declined");
+ if (declined != null) {
+ setDeclinedEngineNames(declined);
+ }
}
public Long getStorageVersion() {
return this.storageVersion;
}
public void setStorageVersion(Long version) {
this.storageVersion = version;
}
public ExtendedJSONObject getEngines() {
return engines;
}
- @SuppressWarnings("unchecked")
- public void declineEngine(String engine) {
- if (this.declined == null) {
- JSONArray replacement = new JSONArray();
- replacement.add(engine);
- setDeclinedEngineNames(replacement);
- return;
- }
-
- this.declined.add(engine);
- }
-
- @SuppressWarnings("unchecked")
- public void declineEngineNames(Collection<String> additional) {
- if (this.declined == null) {
- JSONArray replacement = new JSONArray();
- replacement.addAll(additional);
- setDeclinedEngineNames(replacement);
- return;
- }
-
- for (String engine : additional) {
- if (!this.declined.contains(engine)) {
- this.declined.add(engine);
- }
- }
- }
-
- public void setDeclinedEngineNames(JSONArray declined) {
- if (declined == null) {
- this.declined = new JSONArray();
- return;
- }
+ public void setDeclinedEngineNames(HashSet<String> declined) {
this.declined = declined;
}
- /**
- * Return the set of engines that we support (given as an argument)
- * but the user hasn't explicitly declined on another device.
- *
- * Can return the input if the user hasn't declined any engines.
- */
- public Set<String> getNonDeclinedEngineNames(Set<String> supported) {
- if (this.declined == null ||
- this.declined.isEmpty()) {
- return supported;
+ private void setDeclinedEngineNames(@NonNull JSONArray declinedEngines) {
+ for (int i = 0; i < declinedEngines.length(); i++) {
+ try {
+ final String declinedEngine = declinedEngines.getString(i);
+ this.declined.add(declinedEngine);
+ } catch (JSONException e) {
+ // Be lenient in what we accept from the server.
+ Logger.error(LOG_TAG, "Error parsing declined engines array", e);
+ }
}
-
- final Set<String> result = new HashSet<String>(supported);
- result.removeAll(this.declined);
- return result;
}
public void setEngines(ExtendedJSONObject engines) {
if (engines == null) {
engines = new ExtendedJSONObject();
}
this.engines = engines;
final int count = engines.size();
- versions = new HashMap<String, Integer>(count);
- syncIDs = new HashMap<String, String>(count);
- exceptions = new HashMap<String, MetaGlobalException>(count);
+ versions = new HashMap<>(count);
+ syncIDs = new HashMap<>(count);
+ exceptions = new HashMap<>(count);
for (String engineName : engines.keySet()) {
try {
ExtendedJSONObject engineEntry = engines.getObject(engineName);
recordEngineState(engineName, engineEntry);
} catch (NonObjectJSONException e) {
Logger.error(LOG_TAG, "Engine field for " + engineName + " in meta/global is not an object.");
recordEngineState(engineName, new ExtendedJSONObject()); // Doesn't have a version or syncID, for example, so will be server wiped.
}
@@ -196,17 +164,17 @@ public class MetaGlobal {
/**
* Take a JSON object corresponding to the 'engines' field for the provided engine name,
* updating {@link #syncIDs} and {@link #versions} accordingly.
*
* If the record is malformed, an entry is added to {@link #exceptions}, to be rethrown
* during validation.
*/
- protected void recordEngineState(String engineName, ExtendedJSONObject engineEntry) {
+ private void recordEngineState(String engineName, ExtendedJSONObject engineEntry) {
if (engineEntry == null) {
throw new IllegalArgumentException("engineEntry cannot be null.");
}
// Record syncID first, so that engines with bad versions are recorded.
try {
String syncID = engineEntry.getString("syncID");
if (syncID == null) {
@@ -247,32 +215,32 @@ public class MetaGlobal {
*
* @return a collection of engine names or <code>null</code> if meta/global
* was malformed.
*/
public Set<String> getEnabledEngineNames() {
if (engines == null) {
return null;
}
- return new HashSet<String>(engines.keySet());
+ return new HashSet<>(engines.keySet());
}
@SuppressWarnings("unchecked")
public Set<String> getDeclinedEngineNames() {
if (declined == null) {
return null;
}
return new HashSet<String>(declined);
}
/**
* Returns if the server settings and local settings match.
* Throws a specific MetaGlobalException if that's not the case.
*/
- public void verifyEngineSettings(String engineName, EngineSettings engineSettings)
+ /* package-private */ void verifyEngineSettings(String engineName, EngineSettings engineSettings)
throws MetaGlobalException {
// We use syncIDs as our canary.
if (syncIDs == null) {
throw new IllegalStateException("No meta/global record yet processed.");
}
if (engineSettings == null) {
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/SyncConfiguration.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/SyncConfiguration.java
@@ -22,17 +22,17 @@ import org.mozilla.gecko.sync.stage.Glob
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
public class SyncConfiguration {
private static final String LOG_TAG = "SyncConfiguration";
// These must be set in GlobalSession's constructor.
- public URI clusterURL;
+ private URI clusterURL;
public KeyBundle syncKeyBundle;
public InfoConfiguration infoConfiguration;
public CollectionKeys collectionKeys;
public InfoCollections infoCollections;
public MetaGlobal metaGlobal;
public String syncID;
@@ -44,17 +44,17 @@ public class SyncConfiguration {
* <p>
* Can contain engines Android Sync is not currently aware of, such as "prefs"
* or "addons".
* <p>
* Copied from latest downloaded meta/global record and used to generate a
* fresh meta/global record for upload.
*/
public Set<String> enabledEngineNames;
- public Set<String> declinedEngineNames = new HashSet<String>();
+ public HashSet<String> declinedEngineNames = new HashSet<>();
/**
* Names of stages to sync <it>this sync</it>, or <code>null</code> to sync
* all known stages.
* <p>
* Generated <it>each sync</it> from extras bundle passed to
* <code>SyncAdapter.onPerformSync</code> and not persisted.
* <p>
@@ -79,44 +79,44 @@ public class SyncConfiguration {
* If no engine sync state changes have been made by the user, userSelectedEngines
* will be null, and Sync will proceed normally.
*
* If the user has made changes to engine syncing state, each engine will sync
* according to the sync state specified in userSelectedEngines and propagate that
* state to meta/global, to be uploaded.
*/
public Map<String, Boolean> userSelectedEngines;
- public long userSelectedEnginesTimestamp;
+ /* package-private */ long userSelectedEnginesTimestamp;
public SharedPreferences prefs;
protected final AuthHeaderProvider authHeaderProvider;
public static final String PREF_PREFS_VERSION = "prefs.version";
public static final long CURRENT_PREFS_VERSION = 1;
- public static final String CLIENTS_COLLECTION_TIMESTAMP = "serverClientsTimestamp"; // When the collection was touched.
- public static final String CLIENT_RECORD_TIMESTAMP = "serverClientRecordTimestamp"; // When our record was touched.
- public static final String MIGRATION_SENTINEL_CHECK_TIMESTAMP = "migrationSentinelCheckTimestamp"; // When we last looked in meta/fxa_credentials.
- public static final String BOOKMARK_VALIDATION_CHECK_TIMESTAMP = "bookmarkValidationCheckTimestamp"; // Last time we considered performing validation
+ private static final String CLIENTS_COLLECTION_TIMESTAMP = "serverClientsTimestamp"; // When the collection was touched.
+ private static final String CLIENT_RECORD_TIMESTAMP = "serverClientRecordTimestamp"; // When our record was touched.
+ private static final String MIGRATION_SENTINEL_CHECK_TIMESTAMP = "migrationSentinelCheckTimestamp"; // When we last looked in meta/fxa_credentials.
+ private static final String BOOKMARK_VALIDATION_CHECK_TIMESTAMP = "bookmarkValidationCheckTimestamp"; // Last time we considered performing validation
- public static final String PREF_CLUSTER_URL = "clusterURL";
+ private static final String PREF_CLUSTER_URL = "clusterURL";
public static final String PREF_SYNC_ID = "syncID";
public static final String PREF_ENABLED_ENGINE_NAMES = "enabledEngineNames";
public static final String PREF_DECLINED_ENGINE_NAMES = "declinedEngineNames";
public static final String PREF_USER_SELECTED_ENGINES_TO_SYNC = "userSelectedEngines";
- public static final String PREF_USER_SELECTED_ENGINES_TO_SYNC_TIMESTAMP = "userSelectedEnginesTimestamp";
+ private static final String PREF_USER_SELECTED_ENGINES_TO_SYNC_TIMESTAMP = "userSelectedEnginesTimestamp";
public static final String PREF_CLUSTER_URL_IS_STALE = "clusterurlisstale";
public static final String PREF_ACCOUNT_GUID = "account.guid";
- public static final String PREF_CLIENT_NAME = "account.clientName";
- public static final String PREF_NUM_CLIENTS = "account.numClients";
- public static final String PREF_CLIENT_DATA_TIMESTAMP = "account.clientDataTimestamp";
+ /* package-private */ static final String PREF_CLIENT_NAME = "account.clientName";
+ /* package-private */ static final String PREF_NUM_CLIENTS = "account.numClients";
+ /* package-private */ static final String PREF_CLIENT_DATA_TIMESTAMP = "account.clientDataTimestamp";
private static final String API_VERSION = "1.5";
public SyncConfiguration(String username, AuthHeaderProvider authHeaderProvider, SharedPreferences prefs) {
this.username = username;
this.authHeaderProvider = authHeaderProvider;
this.prefs = prefs;
this.loadFromPrefs(prefs);
@@ -163,44 +163,44 @@ public class SyncConfiguration {
*
* @param prefs
* SharedPreferences that the engines are associated with.
* @param pref
* The preference name to use. E.g, PREF_ENABLED_ENGINE_NAMES.
* @return Set<String> of the enabled engine names if they have been stored,
* or null otherwise.
*/
- protected static Set<String> getEngineNamesFromPref(SharedPreferences prefs, String pref) {
+ private static HashSet<String> getEngineNamesFromPref(SharedPreferences prefs, String pref) {
final String json = prefs.getString(pref, null);
if (json == null) {
return null;
}
try {
final ExtendedJSONObject o = new ExtendedJSONObject(json);
- return new HashSet<String>(o.keySet());
+ return new HashSet<>(o.keySet());
} catch (Exception e) {
return null;
}
}
/**
* Returns the set of engine names that the user has enabled. If none
* have been stored in prefs, <code>null</code> is returned.
*/
public static Set<String> getEnabledEngineNames(SharedPreferences prefs) {
return getEngineNamesFromPref(prefs, PREF_ENABLED_ENGINE_NAMES);
}
/**
* Returns the set of engine names that the user has declined.
*/
- public static Set<String> getDeclinedEngineNames(SharedPreferences prefs) {
- final Set<String> names = getEngineNamesFromPref(prefs, PREF_DECLINED_ENGINE_NAMES);
+ private static HashSet<String> getDeclinedEngineNames(SharedPreferences prefs) {
+ final HashSet<String> names = getEngineNamesFromPref(prefs, PREF_DECLINED_ENGINE_NAMES);
if (names == null) {
- return new HashSet<String>();
+ return new HashSet<>();
}
return names;
}
/**
* Gets the engines whose sync states have been changed by the user through the
* SelectEnginesActivity.
*
@@ -212,19 +212,18 @@ public class SyncConfiguration {
public static Map<String, Boolean> getUserSelectedEngines(SharedPreferences prefs) {
String json = prefs.getString(PREF_USER_SELECTED_ENGINES_TO_SYNC, null);
if (json == null) {
return null;
}
try {
ExtendedJSONObject o = new ExtendedJSONObject(json);
Map<String, Boolean> map = new HashMap<String, Boolean>();
- for (Entry<String, Object> e : o.entrySet()) {
- String key = e.getKey();
- Boolean value = (Boolean) e.getValue();
+ for (String key : o.keySet()) {
+ Boolean value = (Boolean) o.get(key);
map.put(key, value);
// Forms depends on history. Add forms if history is selected.
if ("history".equals(key)) {
map.put("forms", value);
}
}
// Sanity check: remove forms if history does not exist.
if (!map.containsKey("history")) {
@@ -267,17 +266,17 @@ public class SyncConfiguration {
String json = jObj.toJSONString();
long currentTime = System.currentTimeMillis();
Editor edit = prefs.edit();
edit.putString(PREF_USER_SELECTED_ENGINES_TO_SYNC, json);
edit.putString(PREF_DECLINED_ENGINE_NAMES, setToJSONObjectString(declined));
edit.putLong(PREF_USER_SELECTED_ENGINES_TO_SYNC_TIMESTAMP, currentTime);
Logger.error(LOG_TAG, "Storing user-selected engines at [" + currentTime + "].");
- edit.commit();
+ edit.apply();
}
public void loadFromPrefs(SharedPreferences prefs) {
if (prefs.contains(PREF_CLUSTER_URL)) {
String u = prefs.getString(PREF_CLUSTER_URL, null);
try {
clusterURL = new URI(u);
Logger.trace(LOG_TAG, "Set clusterURL from bundle: " + u);
@@ -305,17 +304,17 @@ public class SyncConfiguration {
private static String setToJSONObjectString(Set<String> set) {
ExtendedJSONObject o = new ExtendedJSONObject();
for (String name : set) {
o.put(name, 0);
}
return o.toJSONString();
}
- public void persistToPrefs(SharedPreferences prefs) {
+ private void persistToPrefs(SharedPreferences prefs) {
Editor edit = prefs.edit();
if (clusterURL == null) {
edit.remove(PREF_CLUSTER_URL);
} else {
edit.putString(PREF_CLUSTER_URL, clusterURL.toASCIIString());
}
if (syncID != null) {
edit.putString(PREF_SYNC_ID, syncID);
@@ -331,57 +330,53 @@ public class SyncConfiguration {
edit.putString(PREF_DECLINED_ENGINE_NAMES, setToJSONObjectString(declinedEngineNames));
}
if (userSelectedEngines == null) {
edit.remove(PREF_USER_SELECTED_ENGINES_TO_SYNC);
edit.remove(PREF_USER_SELECTED_ENGINES_TO_SYNC_TIMESTAMP);
}
// Don't bother saving userSelectedEngines - these should only be changed by
// SelectEnginesActivity.
- edit.commit();
+ edit.apply();
// TODO: keys.
}
public AuthHeaderProvider getAuthHeaderProvider() {
return authHeaderProvider;
}
- public CollectionKeys getCollectionKeys() {
+ /* package-private */ CollectionKeys getCollectionKeys() {
return collectionKeys;
}
public void setCollectionKeys(CollectionKeys k) {
collectionKeys = k;
}
/**
* Return path to storage endpoint without trailing slash.
*
* @return storage endpoint without trailing slash.
*/
public String storageURL() {
return clusterURL + "/storage";
}
- protected String infoBaseURL() {
+ private String infoBaseURL() {
return clusterURL + "/info/";
}
- public String infoCollectionsURL() {
+ /* package-private */ String infoCollectionsURL() {
return infoBaseURL() + "collections";
}
- public String infoConfigurationURL() {
+ /* package-private */ String infoConfigurationURL() {
return infoBaseURL() + "configuration";
}
- public String infoCollectionCountsURL() {
- return infoBaseURL() + "collection_counts";
- }
-
public String metaURL() {
return storageURL() + "/meta/global";
}
public URI collectionURI(String collection) throws URISyntaxException {
return new URI(storageURL() + "/" + collection);
}
@@ -396,35 +391,28 @@ public class SyncConfiguration {
params.append("full=1");
}
uriParams = params.toString();
}
String uri = storageURL() + "/" + collection + uriParams;
return new URI(uri);
}
- public URI wboURI(String collection, String id) throws URISyntaxException {
+ /* package-private */ URI wboURI(String collection, String id) throws URISyntaxException {
return new URI(storageURL() + "/" + collection + "/" + id);
}
- public URI keysURI() throws URISyntaxException {
+ /* package-private */ URI keysURI() throws URISyntaxException {
return wboURI("crypto", "keys");
}
- public URI getClusterURL() {
+ /* package-private */ URI getClusterURL() {
return clusterURL;
}
- public String getClusterURLString() {
- if (clusterURL == null) {
- return null;
- }
- return clusterURL.toASCIIString();
- }
-
public void setClusterURL(URI u) {
this.clusterURL = u;
}
/**
* Used for direct management of related prefs.
*/
public Editor getEditor() {
@@ -446,40 +434,32 @@ public class SyncConfiguration {
public void persistServerClientsTimestamp(long timestamp) {
getEditor().putLong(SyncConfiguration.CLIENTS_COLLECTION_TIMESTAMP, timestamp).commit();
}
public long getPersistedServerClientsTimestamp() {
return getPrefs().getLong(SyncConfiguration.CLIENTS_COLLECTION_TIMESTAMP, 0L);
}
- public void persistLastMigrationSentinelCheckTimestamp(long timestamp) {
- getEditor().putLong(SyncConfiguration.MIGRATION_SENTINEL_CHECK_TIMESTAMP, timestamp).commit();
- }
-
- public long getLastMigrationSentinelCheckTimestamp() {
- return getPrefs().getLong(SyncConfiguration.MIGRATION_SENTINEL_CHECK_TIMESTAMP, 0L);
- }
-
public void persistLastValidationCheckTimestamp(long timestamp) {
getEditor().putLong(SyncConfiguration.BOOKMARK_VALIDATION_CHECK_TIMESTAMP, timestamp).commit();
}
public long getLastValidationCheckTimestamp() {
return getPrefs().getLong(SyncConfiguration.BOOKMARK_VALIDATION_CHECK_TIMESTAMP, 0L);
}
- public void purgeCryptoKeys() {
+ /* package-private */ void purgeCryptoKeys() {
if (collectionKeys != null) {
collectionKeys.clear();
}
persistedCryptoKeys().purge();
}
- public void purgeMetaGlobal() {
+ /* package-private */ void purgeMetaGlobal() {
metaGlobal = null;
persistedMetaGlobal().purge();
}
public PersistedCrypto5Keys persistedCryptoKeys() {
return new PersistedCrypto5Keys(getPrefs(), syncKeyBundle);
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/SynchronizerConfiguration.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/SynchronizerConfiguration.java
@@ -1,41 +1,42 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync;
import android.content.SharedPreferences.Editor;
+import org.json.JSONException;
import org.mozilla.gecko.background.common.PrefsBranch;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
import java.io.IOException;
public class SynchronizerConfiguration {
private static final String LOG_TAG = "SynczrConfiguration";
public String syncID;
public RepositorySessionBundle remoteBundle;
public RepositorySessionBundle localBundle;
- public SynchronizerConfiguration(PrefsBranch config) throws NonObjectJSONException, IOException {
+ public SynchronizerConfiguration(PrefsBranch config) throws JSONException {
this.load(config);
}
public SynchronizerConfiguration(String syncID, RepositorySessionBundle remoteBundle, RepositorySessionBundle localBundle) {
this.syncID = syncID;
this.remoteBundle = remoteBundle;
this.localBundle = localBundle;
}
// This should get partly shuffled back into SyncConfiguration, I think.
- public void load(PrefsBranch config) throws NonObjectJSONException, IOException {
+ public void load(PrefsBranch config) throws JSONException {
if (config == null) {
throw new IllegalArgumentException("config cannot be null.");
}
String remoteJSON = config.getString("remote", null);
String localJSON = config.getString("local", null);
RepositorySessionBundle rB = new RepositorySessionBundle(remoteJSON);
RepositorySessionBundle lB = new RepositorySessionBundle(localJSON);
if (remoteJSON == null) {
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/Utils.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/Utils.java
@@ -2,54 +2,62 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URLDecoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.TreeMap;
import java.util.concurrent.Executor;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.mozilla.apache.commons.codec.binary.Base32;
import org.mozilla.apache.commons.codec.binary.Base64;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.nativecode.NativeCrypto;
import org.mozilla.gecko.sync.setup.Constants;
import org.mozilla.gecko.util.IOUtils;
import org.mozilla.gecko.util.StringUtils;
import android.content.Context;
import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
public class Utils {
private static final String LOG_TAG = "Utils";
private static final SecureRandom sharedSecureRandom = new SecureRandom();
// See <http://developer.android.com/reference/android/content/Context.html#getSharedPreferences%28java.lang.String,%20int%29>
public static final int SHARED_PREFERENCES_MODE = 0;
+ private final static int STRING_READER_BUFFER = 8 * 1024;
+
public static String generateGuid() {
byte[] encodedBytes = Base64.encodeBase64(generateRandomBytes(9), false);
return new String(encodedBytes, StringUtils.UTF_8).replace("+", "-").replace("/", "_");
}
/**
* Helper to generate secure random bytes.
*
@@ -114,16 +122,91 @@ public class Utils {
for (byte[] array : rest) {
System.arraycopy(array, 0, result, offset, array.length);
offset += array.length;
}
return result;
}
+ // A less efficient version of JSONArray's copying constructor.
+ @Nullable
+ public static JSONArray lossyShallowCopy(JSONArray source) {
+ final JSONArray copy = new JSONArray();
+ for (int i = 0; i < source.length(); i++) {
+ // If we fail to read elements from 'source', ignore them. Hence 'lossy' in the name.
+ final Object elem;
+ try {
+ elem = source.get(i);
+ } catch (JSONException e) {
+ continue;
+ }
+ copy.put(elem);
+ }
+ return copy;
+ }
+
+ public static int jsonArrayIndexOf(@NonNull JSONArray array, @NonNull Object value) {
+ for (int i = 0; i < array.length(); i++) {
+ try {
+ if (value.equals(array.get(i))) {
+ return i;
+ }
+ } catch (JSONException e) {
+ continue;
+ }
+ }
+ return -1;
+ }
+
+ public static boolean jsonArrayHas(JSONArray array, @NonNull Object value) {
+ return jsonArrayIndexOf(array, value) != -1;
+ }
+
+ public static Object getOrThrow(@NonNull JSONArray array, int index) {
+ try {
+ return array.get(index);
+ } catch (JSONException e) {
+ throw new IllegalStateException("Couldn't get from JSONArray by index", e);
+ }
+ }
+
+ public static Object getOrThrow(@NonNull JSONObject obj, @NonNull String key) {
+ try {
+ return obj.get(key);
+ } catch (JSONException e) {
+ throw new IllegalStateException("Couldn't get key " + key + " from JSONObject");
+ }
+ }
+
+ public static void putOrThrow(@NonNull JSONObject obj, @NonNull String key, @NonNull Object value) {
+ try {
+ obj.put(key, value);
+ } catch (JSONException e) {
+ throw new IllegalStateException("Tried putting by a null key", e);
+ }
+ }
+
+ public static String[] jsonArrayToStrings(@NonNull JSONArray array) throws JSONException {
+ final int size = array.length();
+ final String[] copy = new String[size];
+ for (int i = 0; i < size; i++) {
+ copy[i] = array.getString(i);
+ }
+ return copy;
+ }
+
+ public static HashSet<String> jsonArrayToHashSet(@NonNull JSONArray array) throws JSONException {
+ final HashSet<String> out = new HashSet<>(array.length());
+ for (int i = 0; i < array.length(); i++) {
+ out.add(array.getString(i));
+ }
+ return out;
+ }
+
/**
* Utility for Base64 decoding. Should ensure that the correct
* Apache Commons version is used.
*
* @param base64
* An input string. Will be decoded as UTF-8.
* @return
* A byte array of decoded values.
@@ -271,52 +354,16 @@ public class Utils {
if (bucket == null) {
bucket = new ArrayList<String>();
}
bucket.add(value);
map.put(index, bucket);
}
/**
- * Yes, an equality method that's null-safe.
- */
- private static boolean same(Object a, Object b) {
- if (a == b) {
- return true;
- }
- if (a == null || b == null) {
- return false; // If both null, case above applies.
- }
- return a.equals(b);
- }
-
- /**
- * Return true if the two arrays are both null, or are both arrays
- * containing the same elements in the same order.
- */
- public static boolean sameArrays(JSONArray a, JSONArray b) {
- if (a == b) {
- return true;
- }
- if (a == null || b == null) {
- return false;
- }
- final int size = a.size();
- if (size != b.size()) {
- return false;
- }
- for (int i = 0; i < size; ++i) {
- if (!same(a.get(i), b.get(i))) {
- return false;
- }
- }
- return true;
- }
-
- /**
* Takes a URI, extracting URI components.
* @param scheme the URI scheme on which to match.
*/
@SuppressWarnings("deprecation")
public static Map<String, String> extractURIComponents(String scheme, String uri) {
if (uri.indexOf(scheme) != 0) {
throw new IllegalArgumentException("URI scheme does not match: " + scheme);
}
@@ -542,9 +589,21 @@ public class Utils {
public static Executor newSynchronousExecutor() {
return new Executor() {
@Override
public void execute(Runnable runnable) {
runnable.run();
}
};
}
+
+ public static String readerToString(Reader reader) throws IOException {
+ final char[] arr = new char[STRING_READER_BUFFER];
+ final StringBuilder buffer = new StringBuilder();
+ int numCharsRead;
+
+ while ((numCharsRead = reader.read(arr, 0, arr.length)) != -1) {
+ buffer.append(arr, 0, numCharsRead);
+ }
+
+ return buffer.toString();
+ }
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/net/BaseResource.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/net/BaseResource.java
@@ -13,18 +13,18 @@ import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.net.ssl.SSLContext;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONObject;
import org.mozilla.gecko.background.common.GlobalConstants;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpVersion;
@@ -428,33 +428,33 @@ public class BaseResource implements Res
return e;
}
/**
* Helper for turning a JSON object into a payload.
* @throws UnsupportedEncodingException
*/
protected static StringEntity jsonEntity(JSONObject body) {
- return stringEntityWithContentTypeApplicationJSON(body.toJSONString());
+ return stringEntityWithContentTypeApplicationJSON(body.toString());
}
/**
* Helper for turning an extended JSON object into a payload.
* @throws UnsupportedEncodingException
*/
protected static StringEntity jsonEntity(ExtendedJSONObject body) {
return stringEntityWithContentTypeApplicationJSON(body.toJSONString());
}
/**
* Helper for turning a JSON array into a payload.
* @throws UnsupportedEncodingException
*/
protected static HttpEntity jsonEntity(JSONArray toPOST) throws UnsupportedEncodingException {
- return stringEntityWithContentTypeApplicationJSON(toPOST.toJSONString());
+ return stringEntityWithContentTypeApplicationJSON(toPOST.toString());
}
/**
* Best-effort attempt to ensure that the entity has been fully consumed and
* that the underlying stream has been closed.
*
* This releases the connection back to the connection pool.
*
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/net/MozResponse.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/net/MozResponse.java
@@ -6,30 +6,27 @@ package org.mozilla.gecko.sync.net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Scanner;
-import org.json.simple.JSONArray;
-import org.json.simple.parser.JSONParser;
-import org.json.simple.parser.ParseException;
+import org.json.JSONException;
+import org.json.JSONArray;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.ExtendedJSONObject;
-import org.mozilla.gecko.sync.NonArrayJSONException;
-import org.mozilla.gecko.sync.NonObjectJSONException;
+import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.util.IOUtils;
import org.mozilla.gecko.util.StringUtils;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpResponse;
-import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.impl.cookie.DateParseException;
import ch.boye.httpclientandroidlib.impl.cookie.DateUtils;
public class MozResponse {
private static final String LOG_TAG = "MozResponse";
private static final String HEADER_RETRY_AFTER = "retry-after";
@@ -43,20 +40,16 @@ public class MozResponse {
public int getStatusCode() {
return this.response.getStatusLine().getStatusCode();
}
public boolean wasSuccessful() {
return this.getStatusCode() == 200;
}
- public boolean isInvalidAuthentication() {
- return this.getStatusCode() == HttpStatus.SC_UNAUTHORIZED;
- }
-
/**
* Fetch the content type of the HTTP response body.
*
* @return a <code>Header</code> instance, or <code>null</code> if there was
* no body or no valid Content-Type.
*/
public Header getContentType() {
HttpEntity entity = this.response.getEntity();
@@ -89,19 +82,19 @@ public class MozResponse {
/**
* Return the body as a <b>non-null</b> <code>ExtendedJSONObject</code>.
*
* @return A non-null <code>ExtendedJSONObject</code>.
*
* @throws IllegalStateException
* @throws IOException
- * @throws NonObjectJSONException
+ * @throws JSONException
*/
- public ExtendedJSONObject jsonObjectBody() throws IllegalStateException, IOException, NonObjectJSONException {
+ public ExtendedJSONObject jsonObjectBody() throws IllegalStateException, IOException, JSONException {
if (body != null) {
// Do it from the cached String.
return new ExtendedJSONObject(body);
}
HttpEntity entity = this.response.getEntity();
if (entity == null) {
throw new IOException("no entity");
@@ -111,74 +104,62 @@ public class MozResponse {
try {
in = new BufferedReader(new InputStreamReader(entity.getContent(), StringUtils.UTF_8));
return new ExtendedJSONObject(in);
} finally {
IOUtils.safeStreamClose(in);
}
}
- public JSONArray jsonArrayBody() throws NonArrayJSONException, IOException {
- final JSONParser parser = new JSONParser();
- try {
- if (body != null) {
- // Do it from the cached String.
- return (JSONArray) parser.parse(body);
- }
+ public JSONArray jsonArrayBody() throws JSONException, IOException {
+ // Do it from the cached String.
+ // TODO this method could just by `new JSONArray(body())`, except that body() is less strict.
+ if (body != null) {
+ return new JSONArray(body());
+ }
- final HttpEntity entity = this.response.getEntity();
- if (entity == null) {
- throw new IOException("no entity");
- }
+ final HttpEntity entity = this.response.getEntity();
+ if (entity == null) {
+ throw new IOException("no entity");
+ }
- final InputStream content = entity.getContent();
- final Reader in = new BufferedReader(new InputStreamReader(content, "UTF-8"));
- try {
- return (JSONArray) parser.parse(in);
- } finally {
- in.close();
- }
- } catch (ClassCastException | ParseException e) {
- NonArrayJSONException exception = new NonArrayJSONException("value must be a json array");
- exception.initCause(e);
- throw exception;
+ final InputStream content = entity.getContent();
+ final BufferedReader reader = new BufferedReader(new InputStreamReader(content, "UTF-8"));
+ try {
+ final String readString = Utils.readerToString(reader);
+ body = readString;
+ return new JSONArray(readString);
+ } finally {
+ reader.close();
}
}
- protected boolean hasHeader(String h) {
+ /* package-private */ boolean hasHeader(String h) {
return this.response.containsHeader(h);
}
- public MozResponse(HttpResponse res) {
+ /* package-private */ MozResponse(HttpResponse res) {
response = res;
}
- protected String getNonMissingHeader(String h) {
+ /* package-private */ String getNonMissingHeader(String h) {
if (!this.hasHeader(h)) {
return null;
}
final Header header = this.response.getFirstHeader(h);
final String value = header.getValue();
if (missingHeader(value)) {
Logger.warn(LOG_TAG, h + " header present but empty.");
return null;
}
return value;
}
- protected long getLongHeader(String h) throws NumberFormatException {
- final String value = getNonMissingHeader(h);
- if (value == null) {
- return -1L;
- }
- return Long.parseLong(value, 10);
- }
-
- protected int getIntegerHeader(String h) throws NumberFormatException {
+ /* package-private */ int getIntegerHeader(String h) throws NumberFormatException {
final String value = getNonMissingHeader(h);
if (value == null) {
return -1;
}
return Integer.parseInt(value, 10);
}
/**
@@ -200,28 +181,9 @@ public class MozResponse {
final long then = DateUtils.parseDate(retryAfter).getTime();
final long now = System.currentTimeMillis();
return (int)((then - now) / 1000); // Convert milliseconds to seconds.
} catch (DateParseException e) {
Logger.warn(LOG_TAG, "Retry-After header neither integer nor date: " + retryAfter);
return -1;
}
}
-
- /**
- * @return A number of seconds, or -1 if the 'Backoff' header was not
- * present.
- */
- public int backoffInSeconds() throws NumberFormatException {
- return this.getIntegerHeader("backoff");
- }
-
- public void logResponseBody(final String logTag) {
- if (!Logger.LOG_PERSONAL_INFORMATION) {
- return;
- }
- try {
- Logger.pii(logTag, "Response body: " + body());
- } catch (Throwable e) {
- Logger.debug(logTag, "No response body.");
- }
- }
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/net/SyncStorageRecordRequest.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/net/SyncStorageRecordRequest.java
@@ -1,16 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.net;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONObject;
import org.mozilla.gecko.sync.CryptoRecord;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
/**
* Resource class that implements expected headers and processing for Sync.
@@ -54,17 +54,17 @@ public class SyncStorageRecordRequest ex
return new SyncStorageRecordResourceDelegate(request);
}
@SuppressWarnings("unchecked")
public void post(JSONObject body) {
// Let's do this the trivial way for now.
// Note that POSTs should be an array, so we wrap here.
final JSONArray toPOST = new JSONArray();
- toPOST.add(body);
+ toPOST.put(body);
try {
this.resource.post(toPOST);
} catch (UnsupportedEncodingException e) {
this.delegate.handleRequestError(e);
}
}
public void post(JSONArray body) {
@@ -81,15 +81,15 @@ public class SyncStorageRecordRequest ex
try {
this.resource.put(body);
} catch (UnsupportedEncodingException e) {
this.delegate.handleRequestError(e);
}
}
public void post(CryptoRecord record) {
- this.post(record.toJSONObject());
+ this.post(record.toJSONObject().object);
}
public void put(CryptoRecord record) {
- this.put(record.toJSONObject());
+ this.put(record.toJSONObject().object);
}
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/RepositorySessionBundle.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/RepositorySessionBundle.java
@@ -1,28 +1,29 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.repositories;
+import org.json.JSONException;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.NonObjectJSONException;
import java.io.IOException;
public class RepositorySessionBundle {
public static final String LOG_TAG = RepositorySessionBundle.class.getSimpleName();
protected static final String JSON_KEY_TIMESTAMP = "timestamp";
protected final ExtendedJSONObject object;
- public RepositorySessionBundle(String jsonString) throws IOException, NonObjectJSONException {
+ public RepositorySessionBundle(String jsonString) throws JSONException {
object = new ExtendedJSONObject(jsonString);
}
public RepositorySessionBundle(long lastSyncTimestamp) {
object = new ExtendedJSONObject();
this.setTimestamp(lastSyncTimestamp);
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/BookmarksDataAccessor.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/BookmarksDataAccessor.java
@@ -4,19 +4,21 @@
package org.mozilla.gecko.sync.repositories.android;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
+import org.json.JSONException;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.db.BrowserContract;
+import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.repositories.NullCursorException;
import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
import org.mozilla.gecko.sync.repositories.domain.Record;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
@@ -152,24 +154,30 @@ public class BookmarksDataAccessor exten
/**
* Issue a request to the Content Provider to update the positions of the
* records named by the provided GUIDs to the index of their GUID in the
* provided array.
*
* @param childArray
* A sequence of GUID strings.
*/
- public int updatePositions(ArrayList<String> childArray) {
- final int size = childArray.size();
+ public int updatePositions(JSONArray childArray) {
+ final int size = childArray.length();
if (size == 0) {
return 0;
}
Logger.debug(LOG_TAG, "Updating positions for " + size + " items.");
- String[] args = childArray.toArray(new String[size]);
+ final String[] args;
+ try {
+ args = Utils.jsonArrayToStrings(childArray);
+ } catch (JSONException e) {
+ Logger.warn(LOG_TAG, "updatePositions: couldn't map a child array to array of Strings", e);
+ return 0;
+ }
return context.getContentResolver().update(getPositionsUri(), new ContentValues(), null, args);
}
public int bumpModifiedByGUID(Collection<String> ids, long modified) {
final int size = ids.size();
if (size == 0) {
return 0;
}
@@ -348,17 +356,17 @@ public class BookmarksDataAccessor exten
// We might want to indicate that this record is to be inserted as "modified". Incoming records
// might be modified as we're processing them for insertion, and so should be re-uploaded.
// Our data provider layer manages versions, so we don't pass in localVersion explicitly.
if (rec.modifiedBySync) {
cv.put(BrowserContract.Bookmarks.PARAM_INSERT_FROM_SYNC_AS_MODIFIED, true);
}
- cv.put(BrowserContract.Bookmarks.TAGS, rec.tags.toJSONString());
+ cv.put(BrowserContract.Bookmarks.TAGS, rec.tags.toString());
cv.put(BrowserContract.Bookmarks.KEYWORD, rec.keyword);
cv.put(BrowserContract.Bookmarks.PARENT, rec.androidParentID);
cv.put(BrowserContract.Bookmarks.POSITION, rec.androidPosition);
// Note that we don't set the modified timestamp: we allow the
// content provider to do that for us.
return cv;
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/BookmarksSessionHelper.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/BookmarksSessionHelper.java
@@ -5,17 +5,17 @@
package org.mozilla.gecko.sync.repositories.android;
import android.content.ContentUris;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.repositories.InactiveSessionException;
import org.mozilla.gecko.sync.repositories.NoGuidForIdException;
import org.mozilla.gecko.sync.repositories.NullCursorException;
import org.mozilla.gecko.sync.repositories.ParentNotFoundException;
import org.mozilla.gecko.sync.repositories.RecordFilter;
@@ -256,26 +256,26 @@ import java.util.concurrent.ConcurrentHa
// TODO: update our persisted children arrays!
// TODO: if our Android ID just changed, replace parents for all of our children.
parentGuidToIDMap.put(bmk.guid, bmk.androidID);
parentIDToGuidMap.put(bmk.androidID, bmk.guid);
JSONArray childArray = bmk.children;
if (Logger.shouldLogVerbose(LOG_TAG)) {
- Logger.trace(LOG_TAG, bmk.guid + " has children " + childArray.toJSONString());
+ Logger.trace(LOG_TAG, bmk.guid + " has children " + childArray.toString());
}
parentToChildArray.put(bmk.guid, childArray);
// Re-parent.
if (missingParentToChildren.containsKey(bmk.guid)) {
for (String child : missingParentToChildren.get(bmk.guid)) {
// This might return -1; that's OK, the bookmark will
// be properly repositioned later.
- long position = childArray.indexOf(child);
+ final int position = Utils.jsonArrayIndexOf(childArray, child);
dbAccessor.updateParentAndPosition(child, bmk.androidID, position);
needsReparenting--;
}
missingParentToChildren.remove(bmk.guid);
}
}
/**
@@ -640,39 +640,40 @@ import java.util.concurrent.ConcurrentHa
Logger.debug(LOG_TAG, "Have " + parentToChildArray.size() + " folders whose children might need repositioning.");
for (Map.Entry<String, JSONArray> entry : parentToChildArray.entrySet()) {
String guid = entry.getKey();
JSONArray onServer = entry.getValue();
try {
final long folderID = getIDForGUID(guid);
final JSONArray inDB = new JSONArray();
final boolean clean = getChildrenArray(folderID, false, inDB);
- final boolean sameArrays = Utils.sameArrays(onServer, inDB);
+ final boolean sameArrays = onServer.equals(inDB);
// If the local children and the remote children are already
// the same, then we don't need to bump the modified time of the
// parent: we wouldn't upload a different record, so avoid the cycle.
if (!sameArrays) {
int added = 0;
- for (Object o : inDB) {
- if (!onServer.contains(o)) {
- onServer.add(o);
+ for (int i = 0; i < inDB.length(); i++) {
+ final Object o = inDB.get(i);
+ if (!Utils.jsonArrayHas(onServer, o)) {
+ onServer.put(o);
added++;
}
}
Logger.debug(LOG_TAG, "Added " + added + " items locally.");
Logger.debug(LOG_TAG, "Untracking and bumping " + guid + "(" + folderID + ")");
dbAccessor.bumpModified(folderID, RepositorySession.now());
session.untrackGUID(guid);
}
// If the arrays are different, or they're the same but not flushed to disk,
// write them out now.
if (!sameArrays || !clean) {
- dbAccessor.updatePositions(new ArrayList<>(onServer));
+ dbAccessor.updatePositions(onServer);
}
} catch (Exception e) {
Logger.warn(LOG_TAG, "Error repositioning children for " + guid, e);
}
}
}
private void flushQueues(RepositorySessionStoreDelegate delegate) {
@@ -716,17 +717,17 @@ import java.util.concurrent.ConcurrentHa
*/
private void handleParenting(BookmarkRecord bmk) {
if (parentGuidToIDMap.containsKey(bmk.parentID)) {
bmk.androidParentID = parentGuidToIDMap.get(bmk.parentID);
// Might as well set a basic position from the downloaded children array.
JSONArray children = parentToChildArray.get(bmk.parentID);
if (children != null) {
- int index = children.indexOf(bmk.guid);
+ int index = Utils.jsonArrayIndexOf(children, bmk.guid);
if (index >= 0) {
bmk.androidPosition = index;
}
}
}
else {
bmk.androidParentID = parentGuidToIDMap.get("unfiled");
ArrayList<String> children;
@@ -1002,17 +1003,17 @@ import java.util.concurrent.ConcurrentHa
if (!isFolder) {
return null;
}
long androidID = parentGuidToIDMap.get(recordGUID);
JSONArray childArray = new JSONArray();
getChildrenArray(androidID, persist, childArray);
- Logger.debug(LOG_TAG, "Fetched " + childArray.size() + " children for " + recordGUID);
+ Logger.debug(LOG_TAG, "Fetched " + childArray.length() + " children for " + recordGUID);
return childArray;
}
private static boolean rowIsFolder(Cursor cur) {
return getTypeFromCursor(cur) == BrowserContract.Bookmarks.TYPE_FOLDER;
}
private static int getTypeFromCursor(Cursor cur) {
@@ -1075,24 +1076,24 @@ import java.util.concurrent.ConcurrentHa
if (atPos > 1 || pos != i) {
changed = true;
}
++i;
for (String guid : entry.getValue()) {
if (!forbiddenGUID(guid)) {
- childArray.add(guid);
+ childArray.put(guid);
}
}
}
if (Logger.shouldLogVerbose(LOG_TAG)) {
// Don't JSON-encode unless we're logging.
- Logger.trace(LOG_TAG, "Output child array: " + childArray.toJSONString());
+ Logger.trace(LOG_TAG, "Output child array: " + childArray.toString());
}
if (!changed) {
Logger.debug(LOG_TAG, "Nothing moved! Database reflects child array.");
return true;
}
if (!persist) {
@@ -1130,17 +1131,17 @@ import java.util.concurrent.ConcurrentHa
Logger.pii(LOG_TAG, "> Title: " + rec.title);
Logger.pii(LOG_TAG, "> Type: " + rec.type);
Logger.pii(LOG_TAG, "> URI: " + rec.bookmarkURI);
Logger.pii(LOG_TAG, "> Position: " + rec.androidPosition);
if (rec.isFolder()) {
Logger.pii(LOG_TAG, "FOLDER: Children are " +
(rec.children == null ?
"null" :
- rec.children.toJSONString()));
+ rec.children.toString()));
}
}
} catch (Exception e) {
Logger.debug(LOG_TAG, "Exception logging bookmark record " + rec, e);
}
return rec;
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/ClientsDatabaseAccessor.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/ClientsDatabaseAccessor.java
@@ -8,17 +8,17 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.fxa.devices.FxAccountDevice;
import org.mozilla.gecko.sync.CommandProcessor.Command;
import org.mozilla.gecko.sync.repositories.NullCursorException;
import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
import org.mozilla.gecko.sync.setup.Constants;
@@ -45,17 +45,17 @@ public class ClientsDatabaseAccessor {
public void store(Collection<ClientRecord> records) {
for (ClientRecord record : records) {
this.store(record);
}
}
public void store(String accountGUID, Command command) throws NullCursorException {
- db.store(accountGUID, command.commandType, command.args.toJSONString(), command.flowID);
+ db.store(accountGUID, command.commandType, command.args.toString(), command.flowID);
}
public ClientRecord fetchClient(String accountGUID) throws NullCursorException {
final Cursor cur = db.fetchClientsCursor(accountGUID, getProfileId());
try {
if (!cur.moveToFirst()) {
return null;
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/HistoryDataAccessor.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/HistoryDataAccessor.java
@@ -1,20 +1,21 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.repositories.android;
import java.util.ArrayList;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONObject;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.db.BrowserContract;
+import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.repositories.NullCursorException;
import org.mozilla.gecko.sync.repositories.domain.HistoryRecord;
import org.mozilla.gecko.sync.repositories.domain.Record;
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
@@ -43,17 +44,17 @@ public class HistoryDataAccessor extends
if (rec.visits != null) {
final JSONArray visits = rec.visits;
final long mostRecent = getLastVisited(visits);
// Fennec stores history timestamps in milliseconds, and visit timestamps in microseconds.
// The rest of Sync works in microseconds. This is the conversion point for records coming form Sync.
cv.put(BrowserContract.History.DATE_LAST_VISITED, mostRecent / 1000);
cv.put(BrowserContract.History.REMOTE_DATE_LAST_VISITED, mostRecent / 1000);
- cv.put(BrowserContract.History.VISITS, visits.size());
+ cv.put(BrowserContract.History.VISITS, visits.length());
}
return cv;
}
@Override
protected String[] getAllColumns() {
return BrowserContractHelpers.HistoryColumns;
}
@@ -151,18 +152,18 @@ public class HistoryDataAccessor extends
/**
* Helper method used to find largest <code>VisitsHelper.SYNC_DATE_KEY</code> value in a provided JSONArray.
*
* @param visits Array of objects which will be searched.
* @return largest value of <code>VisitsHelper.SYNC_DATE_KEY</code>.
*/
private long getLastVisited(JSONArray visits) {
long mostRecent = 0;
- for (int i = 0; i < visits.size(); i++) {
- final JSONObject visit = (JSONObject) visits.get(i);
- long visitDate = (Long) visit.get(VisitsHelper.SYNC_DATE_KEY);
+ for (int i = 0; i < visits.length(); i++) {
+ final JSONObject visit = (JSONObject) Utils.getOrThrow(visits, i);
+ long visitDate = (Long) Utils.getOrThrow(visit, VisitsHelper.SYNC_DATE_KEY);
if (visitDate > mostRecent) {
mostRecent = visitDate;
}
}
return mostRecent;
}
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/RepoUtils.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/RepoUtils.java
@@ -6,17 +6,18 @@ package org.mozilla.gecko.sync.repositor
import android.content.ContentProviderClient;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.RemoteException;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
+import org.json.JSONException;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.NonArrayJSONException;
import org.mozilla.gecko.sync.repositories.NullCursorException;
import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
import org.mozilla.gecko.sync.repositories.domain.HistoryRecord;
@@ -28,83 +29,83 @@ public class RepoUtils {
/**
* A helper class for monotonous SQL querying. Does timing and logging,
* offers a utility to throw on a null cursor.
*
* @author rnewman
*
*/
- public static class QueryHelper {
+ /* package-private */ static class QueryHelper {
private final Context context;
private final Uri uri;
private final String tag;
- public QueryHelper(Context context, Uri uri, String tag) {
+ /* package-private */ QueryHelper(Context context, Uri uri, String tag) {
this.context = context;
this.uri = uri;
this.tag = tag;
}
// For ContentProvider queries.
- public Cursor safeQuery(String label, String[] projection,
+ /* package-private */ Cursor safeQuery(String label, String[] projection,
String selection, String[] selectionArgs, String sortOrder) throws NullCursorException {
long queryStart = android.os.SystemClock.uptimeMillis();
Cursor c = context.getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
return checkAndLogCursor(label, queryStart, c);
}
public Cursor safeQuery(String[] projection, String selection, String[] selectionArgs, String sortOrder) throws NullCursorException {
return this.safeQuery(null, projection, selection, selectionArgs, sortOrder);
}
// For ContentProviderClient queries.
- public Cursor safeQuery(ContentProviderClient client, String label, String[] projection,
+ /* package-private */ Cursor safeQuery(ContentProviderClient client, String label, String[] projection,
String selection, String[] selectionArgs, String sortOrder) throws NullCursorException, RemoteException {
long queryStart = android.os.SystemClock.uptimeMillis();
Cursor c = client.query(uri, projection, selection, selectionArgs, sortOrder);
return checkAndLogCursor(label, queryStart, c);
}
// For SQLiteOpenHelper queries.
- public Cursor safeQuery(SQLiteDatabase db, String label, String table, String[] columns,
+ /* package-private */ Cursor safeQuery(SQLiteDatabase db, String label, String table, String[] columns,
String selection, String[] selectionArgs,
String groupBy, String having, String orderBy, String limit) throws NullCursorException {
long queryStart = android.os.SystemClock.uptimeMillis();
Cursor c = db.query(table, columns, selection, selectionArgs, groupBy, having, orderBy, limit);
return checkAndLogCursor(label, queryStart, c);
}
- public Cursor safeQuery(SQLiteDatabase db, String label, String table, String[] columns,
+ /* package-private */ Cursor safeQuery(SQLiteDatabase db, String label, String table, String[] columns,
String selection, String[] selectionArgs) throws NullCursorException {
return safeQuery(db, label, table, columns, selection, selectionArgs, null, null, null, null);
}
private Cursor checkAndLogCursor(String label, long queryStart, Cursor c) throws NullCursorException {
long queryEnd = android.os.SystemClock.uptimeMillis();
String logLabel = (label == null) ? tag : (tag + label);
RepoUtils.queryTimeLogger(logLabel, queryStart, queryEnd);
return checkNullCursor(logLabel, c);
}
- public Cursor checkNullCursor(String logLabel, Cursor cursor) throws NullCursorException {
+ /* package-private */ Cursor checkNullCursor(String logLabel, Cursor cursor) throws NullCursorException {
if (cursor == null) {
Logger.error(tag, "Got null cursor exception in " + logLabel);
throw new NullCursorException(null);
}
return cursor;
}
}
/**
* This method exists because the behavior of <code>cur.getString()</code> is undefined
* when the value in the database is <code>NULL</code>.
* This method will return <code>null</code> in that case.
*/
- public static String optStringFromCursor(final Cursor cur, final String colId) {
+ /* package-private */ static String optStringFromCursor(final Cursor cur, final String colId) {
final int col = cur.getColumnIndex(colId);
if (cur.isNull(col)) {
return null;
}
return cur.getString(col);
}
/**
@@ -127,32 +128,29 @@ public class RepoUtils {
public static JSONArray getJSONArrayFromCursor(Cursor cur, String colId) {
String jsonArrayAsString = getStringFromCursor(cur, colId);
if (jsonArrayAsString == null) {
return new JSONArray();
}
try {
return ExtendedJSONObject.parseJSONArray(getStringFromCursor(cur, colId));
- } catch (NonArrayJSONException e) {
- Logger.error(LOG_TAG, "JSON parsing error for " + colId, e);
- return null;
- } catch (IOException e) {
+ } catch (JSONException e) {
Logger.error(LOG_TAG, "JSON parsing error for " + colId, e);
return null;
}
}
/**
* Return true if the provided URI is non-empty and acceptable to Fennec
* (i.e., not an undesirable scheme).
*
* This code is pilfered from Fennec, which pilfered from Places.
*/
- public static boolean isValidHistoryURI(String uri) {
+ /* package-private */ static boolean isValidHistoryURI(String uri) {
if (uri == null || uri.length() == 0) {
return false;
}
// First, check the most common cases (HTTP, HTTPS) to avoid most of the work.
if (uri.startsWith("http:") || uri.startsWith("https:")) {
return true;
}
@@ -181,17 +179,17 @@ public class RepoUtils {
}
/**
* Create a HistoryRecord object from a cursor row.
*
* @return a HistoryRecord, or null if this row would produce
* an invalid record (e.g., with a null URI or no visits).
*/
- public static HistoryRecord historyFromMirrorCursor(Cursor cur) {
+ /* package-private */ static HistoryRecord historyFromMirrorCursor(Cursor cur) {
final String guid = getStringFromCursor(cur, BrowserContract.SyncColumns.GUID);
if (guid == null) {
Logger.debug(LOG_TAG, "Skipping history record with null GUID.");
return null;
}
final String historyURI = getStringFromCursor(cur, BrowserContract.History.URL);
if (!isValidHistoryURI(historyURI)) {
@@ -240,45 +238,30 @@ public class RepoUtils {
Logger.trace(LOG_TAG, "Returning client record " + rec.guid + " (" + rec.androidID + ")");
Logger.trace(LOG_TAG, "Client Name: " + rec.name);
Logger.trace(LOG_TAG, "Client Type: " + rec.type);
Logger.trace(LOG_TAG, "Last Modified: " + rec.lastModified);
Logger.trace(LOG_TAG, "Deleted: " + rec.deleted);
}
}
- public static void queryTimeLogger(String methodCallingQuery, long queryStart, long queryEnd) {
+ private static void queryTimeLogger(String methodCallingQuery, long queryStart, long queryEnd) {
long elapsedTime = queryEnd - queryStart;
Logger.debug(LOG_TAG, "Query timer: " + methodCallingQuery + " took " + elapsedTime + "ms.");
}
public static boolean stringsEqual(String a, String b) {
// Check for nulls
if (a == b) return true;
if (a == null && b != null) return false;
if (a != null && b == null) return false;
return a.equals(b);
}
- public static String computeSQLLongInClause(long[] items, String field) {
- final StringBuilder builder = new StringBuilder(field);
- builder.append(" IN (");
- int i = 0;
- for (; i < items.length - 1; ++i) {
- builder.append(items[i]);
- builder.append(", ");
- }
- if (i < items.length) {
- builder.append(items[i]);
- }
- builder.append(")");
- return builder.toString();
- }
-
public static String computeSQLInClause(int items, String field) {
final StringBuilder builder = new StringBuilder(field);
builder.append(" IN (");
int i = 0;
for (; i < items - 1; ++i) {
builder.append("?, ");
}
if (i < items) {
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/VisitsHelper.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/VisitsHelper.java
@@ -6,19 +6,21 @@ package org.mozilla.gecko.sync.repositor
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import android.support.annotation.NonNull;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.mozilla.gecko.db.BrowserContract.Visits;
+import org.mozilla.gecko.sync.Utils;
/**
* This class is used by History Sync code (see <code>AndroidBrowserHistoryDataAccessor</code> and <code>AndroidBrowserHistoryRepositorySession</code>,
* and provides utility functions for working with history visits. Primarily we're either inserting visits
* into local database based on data received from Sync, or we're preparing local visits for upload into Sync.
*/
public class VisitsHelper {
public static final boolean DEFAULT_IS_LOCAL_VALUE = false;
@@ -29,26 +31,26 @@ public class VisitsHelper {
* Returns a list of ContentValues of visits ready for insertion for a provided History GUID.
* Visits must have data and type. See <code>getVisitContentValues</code>.
*
* @param guid History GUID to use when inserting visit records
* @param visits <code>JSONArray</code> list of (date, type) tuples for visits
* @return visits ready for insertion
*/
public static ContentValues[] getVisitsContentValues(@NonNull String guid, @NonNull JSONArray visits) {
- final ContentValues[] visitsToStore = new ContentValues[visits.size()];
- final int visitCount = visits.size();
+ final ContentValues[] visitsToStore = new ContentValues[visits.length()];
+ final int visitCount = visits.length();
if (visitCount == 0) {
return visitsToStore;
}
for (int i = 0; i < visitCount; i++) {
visitsToStore[i] = getVisitContentValues(
- guid, (JSONObject) visits.get(i), DEFAULT_IS_LOCAL_VALUE);
+ guid, (JSONObject) Utils.getOrThrow(visits, i), DEFAULT_IS_LOCAL_VALUE);
}
return visitsToStore;
}
/**
* Maps up to <code>limit</code> visits for a given history GUID to an array of JSONObjects with "date" and "type" keys
*
* @param contentClient <code>ContentProviderClient</code> to use for querying Visits table
@@ -72,20 +74,25 @@ public class VisitsHelper {
if (!cursor.moveToFirst()) {
return visits;
}
final int dateVisitedCol = cursor.getColumnIndexOrThrow(Visits.DATE_VISITED);
final int visitTypeCol = cursor.getColumnIndexOrThrow(Visits.VISIT_TYPE);
while (!cursor.isAfterLast()) {
- insertTupleIntoVisitsUnchecked(visits,
- cursor.getLong(visitTypeCol),
- cursor.getLong(dateVisitedCol)
- );
+ final JSONObject visit = new JSONObject();
+ try {
+ visit.put(SYNC_TYPE_KEY, cursor.getLong(visitTypeCol));
+ visit.put(SYNC_DATE_KEY, cursor.getLong(dateVisitedCol));
+ visits.put(visit);
+ } catch (JSONException e) {
+ // Should never happen.
+ throw new IllegalStateException("SYNC_TYPE_KEY or SYNC_DATE_KEY is null", e);
+ }
cursor.moveToNext();
}
} finally {
cursor.close();
}
return visits;
}
@@ -95,36 +102,28 @@ public class VisitsHelper {
*
* @param visit <code>JSONObject</code> containing visit type and visit date keys for the visit
* @param guid History GUID with with to associate this visit
* @param isLocal Whether or not to mark this visit as local
* @return <code>ContentValues</code> with all visit values necessary for database insertion
* @throws IllegalArgumentException if visit object is missing date or type keys
*/
public static ContentValues getVisitContentValues(@NonNull String guid, @NonNull JSONObject visit, boolean isLocal) {
- if (!visit.containsKey(SYNC_DATE_KEY) || !visit.containsKey(SYNC_TYPE_KEY)) {
+ if (!visit.has(SYNC_DATE_KEY) || !visit.has(SYNC_TYPE_KEY)) {
throw new IllegalArgumentException("Visit missing required keys");
}
final ContentValues cv = new ContentValues();
cv.put(Visits.HISTORY_GUID, guid);
cv.put(Visits.IS_LOCAL, isLocal ? Visits.VISIT_IS_LOCAL : Visits.VISIT_IS_REMOTE);
- cv.put(Visits.VISIT_TYPE, (Long) visit.get(SYNC_TYPE_KEY));
- cv.put(Visits.DATE_VISITED, (Long) visit.get(SYNC_DATE_KEY));
+ cv.put(Visits.VISIT_TYPE, (Long) Utils.getOrThrow(visit, SYNC_TYPE_KEY));
+ cv.put(Visits.DATE_VISITED, (Long) Utils.getOrThrow(visit, SYNC_DATE_KEY));
return cv;
}
- @SuppressWarnings("unchecked")
- private static void insertTupleIntoVisitsUnchecked(@NonNull final JSONArray visits, @NonNull Long type, @NonNull Long date) {
- final JSONObject visit = new JSONObject();
- visit.put(SYNC_TYPE_KEY, type);
- visit.put(SYNC_DATE_KEY, date);
- visits.add(visit);
- }
-
private static Uri visitsUriWithLimit(int limit) {
return BrowserContractHelpers.VISITS_CONTENT_URI
.buildUpon()
.appendQueryParameter("limit", Integer.toString(limit))
.build();
}
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/domain/BookmarkRecord.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/domain/BookmarkRecord.java
@@ -5,35 +5,35 @@
package org.mozilla.gecko.sync.repositories.domain;
import android.support.annotation.Nullable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.NonArrayJSONException;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.repositories.android.RepoUtils;
/**
* Covers the fields used by all bookmark objects.
* @author rnewman
*
*/
public class BookmarkRecord extends Record {
- public static final String PLACES_URI_PREFIX = "places:";
+ private static final String PLACES_URI_PREFIX = "places:";
private static final String LOG_TAG = "BookmarkRecord";
public static final String COLLECTION_NAME = "bookmarks";
- public static final long BOOKMARKS_TTL = -1; // Never ttl bookmarks.
+ private static final long BOOKMARKS_TTL = -1; // Never ttl bookmarks.
public BookmarkRecord(String guid, String collection, long lastModified, boolean deleted) {
super(guid, collection, lastModified, deleted);
this.ttl = BOOKMARKS_TTL;
}
public BookmarkRecord(String guid, String collection, long lastModified) {
this(guid, collection, lastModified, false);
}
@@ -67,37 +67,16 @@ public class BookmarkRecord extends Reco
public JSONArray tags;
@Override
public String toString() {
return "#<Bookmark " + guid + " (" + androidID + "), parent " +
parentID + "/" + androidParentID + "/" + parentName + ">";
}
- // Oh God, this is terribly thread-unsafe. These record objects should be immutable.
- @SuppressWarnings("unchecked")
- protected JSONArray copyChildren() {
- if (this.children == null) {
- return null;
- }
- JSONArray children = new JSONArray();
- children.addAll(this.children);
- return children;
- }
-
- @SuppressWarnings("unchecked")
- protected JSONArray copyTags() {
- if (this.tags == null) {
- return null;
- }
- JSONArray tags = new JSONArray();
- tags.addAll(this.tags);
- return tags;
- }
-
@Override
public Record copyWithIDs(String guid, long androidID) {
BookmarkRecord out = new BookmarkRecord(guid, this.collection, this.lastModified, this.deleted);
out.androidID = androidID;
out.sortIndex = this.sortIndex;
out.ttl = this.ttl;
// Copy BookmarkRecord fields.
@@ -107,18 +86,29 @@ public class BookmarkRecord extends Reco
out.keyword = this.keyword;
out.parentID = this.parentID;
out.parentName = this.parentName;
out.androidParentID = this.androidParentID;
out.type = this.type;
out.androidPosition = this.androidPosition;
out.dateAdded = this.dateAdded;
- out.children = this.copyChildren();
- out.tags = this.copyTags();
+ // We could be operating on a local or a remote record.
+ // In either case, we try our best to copy over the arrays.
+ if (this.children == null) {
+ out.children = null;
+ } else {
+ out.children = Utils.lossyShallowCopy(this.children);
+ }
+
+ if (this.tags == null) {
+ out.tags = null;
+ } else {
+ out.tags = Utils.lossyShallowCopy(this.tags);
+ }
return out;
}
public boolean isBookmark() {
if (type == null) {
return false;
}
@@ -141,17 +131,17 @@ public class BookmarkRecord extends Reco
public boolean isSeparator() {
if (type == null) {
return false;
}
return type.equals("separator");
}
- public boolean isMicrosummary() {
+ private boolean isMicrosummary() {
if (type == null) {
return false;
}
return type.equals("microsummary");
}
public boolean isQuery() {
if (type == null) {
@@ -258,19 +248,16 @@ public class BookmarkRecord extends Reco
this.androidPosition = Long.parseLong((String) p, 10);
} catch (NumberFormatException e) {
return;
}
} else {
Logger.warn(LOG_TAG, "Unsupported position value " + p);
return;
}
- String pos = String.valueOf(this.androidPosition);
- this.bookmarkURI = encodeUnsupportedTypeURI(null, "pos", pos, null, null);
- return;
}
}
@Override
protected void populatePayload(ExtendedJSONObject payload) {
putPayload(payload, "type", this.type);
putPayload(payload, "title", this.title);
putPayload(payload, "description", this.description);
@@ -361,35 +348,35 @@ public class BookmarkRecord extends Reco
if (!RepoUtils.stringsEqual(this.type, other.type)) {
return false;
}
// Check children.
if (isFolder() && (this.children != other.children)) {
trace("BookmarkRecord.equals: this folder: " + this.title + ", " + this.guid);
trace("BookmarkRecord.equals: other: " + other.title + ", " + other.guid);
- if (this.children == null &&
+ if (this.children == null &&
other.children != null) {
trace("Records differ: one children array is null.");
return false;
}
- if (this.children != null &&
+ if (this.children != null &&
other.children == null) {
trace("Records differ: one children array is null.");
return false;
}
- if (this.children.size() != other.children.size()) {
+ if (this.children.length() != other.children.length()) {
trace("Records differ: children arrays differ in size (" +
- this.children.size() + " vs. " + other.children.size() + ").");
+ this.children.length() + " vs. " + other.children.length() + ").");
return false;
}
- for (int i = 0; i < this.children.size(); i++) {
- String child = (String) this.children.get(i);
- if (!other.children.contains(child)) {
+ for (int i = 0; i < this.children.length(); i++) {
+ final String child = (String) Utils.getOrThrow(this.children, i);
+ if (!Utils.jsonArrayHas(other.children, child)) {
trace("Records differ: child " + child + " not found.");
return false;
}
}
}
trace("Checking strings.");
return RepoUtils.stringsEqual(this.title, other.title)
@@ -411,17 +398,17 @@ public class BookmarkRecord extends Reco
// Converts two JSONArrays to strings and checks if they are the same.
// This is only useful for stuff like tags where we aren't actually
// touching the data there (and therefore ordering won't change)
private boolean jsonArrayStringsEqual(JSONArray a, JSONArray b) {
// Check for nulls
if (a == b) return true;
if (a == null && b != null) return false;
if (a != null && b == null) return false;
- return RepoUtils.stringsEqual(a.toJSONString(), b.toJSONString());
+ return RepoUtils.stringsEqual(a.toString(), b.toString());
}
/**
* URL-encode the provided string. If the input is null,
* the empty string is returned.
*
* @param in the string to encode.
* @return a URL-encoded version of the input.
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/domain/ClientRecord.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/domain/ClientRecord.java
@@ -1,15 +1,15 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.repositories.domain;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.NonArrayJSONException;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.repositories.android.RepoUtils;
public class ClientRecord extends Record {
private static final String LOG_TAG = "ClientRecord";
@@ -70,20 +70,20 @@ public class ClientRecord extends Record
}
public ClientRecord() {
this(Utils.generateGuid(), COLLECTION_NAME, 0, false);
}
@Override
protected void initFromPayload(ExtendedJSONObject payload) {
- this.name = (String) payload.get("name");
- this.type = (String) payload.get("type");
+ this.name = payload.getString("name");
+ this.type = payload.getString("type");
try {
- this.version = (String) payload.get("version");
+ this.version = payload.getString("version");
} catch (Exception e) {
// Oh well.
}
try {
commands = payload.getArray("commands");
} catch (NonArrayJSONException e) {
Logger.debug(LOG_TAG, "Got non-array commands in client record " + guid, e);
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/domain/HistoryRecord.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/domain/HistoryRecord.java
@@ -1,18 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.repositories.domain;
import java.util.HashMap;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.NonArrayJSONException;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.repositories.android.RepoUtils;
/**
* Visits are in microsecond precision.
@@ -44,55 +45,50 @@ public class HistoryRecord extends Recor
}
public String title;
public String histURI;
public JSONArray visits;
public long fennecDateVisited;
public long fennecVisitCount;
- @SuppressWarnings("unchecked")
- private JSONArray copyVisits() {
- if (this.visits == null) {
- return null;
- }
- JSONArray out = new JSONArray();
- out.addAll(this.visits);
- return out;
- }
-
@Override
public Record copyWithIDs(String guid, long androidID) {
HistoryRecord out = new HistoryRecord(guid, this.collection, this.lastModified, this.deleted);
out.androidID = androidID;
out.sortIndex = this.sortIndex;
out.ttl = this.ttl;
// Copy HistoryRecord fields.
out.title = this.title;
out.histURI = this.histURI;
out.fennecDateVisited = this.fennecDateVisited;
out.fennecVisitCount = this.fennecVisitCount;
- out.visits = this.copyVisits();
+
+ if (this.visits == null) {
+ out.visits = null;
+ } else {
+ out.visits = Utils.lossyShallowCopy(this.visits);
+ }
return out;
}
@Override
protected void populatePayload(ExtendedJSONObject payload) {
putPayload(payload, "id", this.guid);
putPayload(payload, "title", this.title);
putPayload(payload, "histUri", this.histURI); // TODO: encoding?
payload.put("visits", this.visits);
}
@Override
protected void initFromPayload(ExtendedJSONObject payload) {
- this.histURI = (String) payload.get("histUri");
- this.title = (String) payload.get("title");
+ this.histURI = payload.getString("histUri");
+ this.title = payload.getString("title");
try {
this.visits = payload.getArray("visits");
} catch (NonArrayJSONException e) {
Logger.error(LOG_TAG, "Got non-array visits in history record " + this.guid, e);
this.visits = new JSONArray();
}
}
@@ -119,19 +115,27 @@ public class HistoryRecord extends Recor
Logger.debug(LOG_TAG, "Not a HistoryRecord: " + o.getClass());
return false;
}
HistoryRecord other = (HistoryRecord) o;
if (!super.equalPayloads(other)) {
Logger.debug(LOG_TAG, "super.equalPayloads returned false.");
return false;
}
+ boolean visitsEqual;
+ try {
+ visitsEqual = checkVisitsEquals(other);
+ } catch (JSONException e) {
+ // This might happen if visit entries are malformed. Consider sets as different.
+ Logger.error(LOG_TAG, "Exception while comparing visits equality", e);
+ visitsEqual = false;
+ }
return RepoUtils.stringsEqual(this.title, other.title) &&
RepoUtils.stringsEqual(this.histURI, other.histURI) &&
- checkVisitsEquals(other);
+ visitsEqual;
}
@Override
public boolean equalAndroidIDs(Record other) {
return super.equalAndroidIDs(other) &&
this.equalFennecVisits(other);
}
@@ -139,57 +143,57 @@ public class HistoryRecord extends Recor
if (!(other instanceof HistoryRecord)) {
return false;
}
HistoryRecord h = (HistoryRecord) other;
return this.fennecDateVisited == h.fennecDateVisited &&
this.fennecVisitCount == h.fennecVisitCount;
}
- private boolean checkVisitsEquals(HistoryRecord other) {
+ private boolean checkVisitsEquals(HistoryRecord other) throws JSONException {
Logger.debug(LOG_TAG, "Checking visits.");
if (Logger.LOG_PERSONAL_INFORMATION) {
// Don't JSON-encode unless we're logging.
- Logger.pii(LOG_TAG, ">> Mine: " + ((this.visits == null) ? "null" : this.visits.toJSONString()));
- Logger.pii(LOG_TAG, ">> Theirs: " + ((other.visits == null) ? "null" : other.visits.toJSONString()));
+ Logger.pii(LOG_TAG, ">> Mine: " + ((this.visits == null) ? "null" : this.visits.toString()));
+ Logger.pii(LOG_TAG, ">> Theirs: " + ((other.visits == null) ? "null" : other.visits.toString()));
}
// Handle nulls.
if (this.visits == other.visits) {
return true;
}
// Now they can't both be null.
- int aSize = this.visits == null ? 0 : this.visits.size();
- int bSize = other.visits == null ? 0 : other.visits.size();
+ int aSize = this.visits == null ? 0 : this.visits.length();
+ int bSize = other.visits == null ? 0 : other.visits.length();
if (aSize != bSize) {
return false;
}
// Now neither of them can be null.
// TODO: do this by maintaining visits as a sorted array.
HashMap<Long, Long> otherVisits = new HashMap<Long, Long>();
for (int i = 0; i < bSize; i++) {
JSONObject visit = (JSONObject) other.visits.get(i);
- otherVisits.put((Long) visit.get("date"), (Long) visit.get("type"));
+ otherVisits.put(visit.getLong("date"), visit.getLong("type"));
}
for (int i = 0; i < aSize; i++) {
JSONObject visit = (JSONObject) this.visits.get(i);
- if (!otherVisits.containsKey(visit.get("date"))) {
+ if (!otherVisits.containsKey(visit.getLong("date"))) {
return false;
}
- Long otherDate = (Long) visit.get("date");
+ Long otherDate = visit.getLong("date");
Long otherType = otherVisits.get(otherDate);
if (otherType == null) {
return false;
}
- if (!otherType.equals((Long) visit.get("type"))) {
+ if (!otherType.equals(visit.getLong("type"))) {
return false;
}
}
return true;
}
//
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/domain/Record.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/domain/Record.java
@@ -3,17 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.repositories.domain;
import android.support.annotation.Nullable;
import java.io.UnsupportedEncodingException;
-import org.json.simple.JSONObject;
+import org.json.JSONObject;
import org.mozilla.gecko.sync.CryptoRecord;
import org.mozilla.gecko.sync.ExtendedJSONObject;
/**
* Record is the abstract base class for all entries that Sync processes:
* bookmarks, passwords, history, and such.
*
* A Record can be initialized from or serialized to a CryptoRecord for
@@ -249,17 +249,17 @@ public abstract class Record {
}
@SuppressWarnings("static-method")
public String toJSONString() {
throw new RuntimeException("Cannot JSONify non-CryptoRecord Records.");
}
@SuppressWarnings("static-method")
- public JSONObject toJSONObject() {
+ public ExtendedJSONObject toJSONObject() {
throw new RuntimeException("Cannot JSONify non-CryptoRecord Records.");
}
public static byte[] stringToJSONBytes(String in) {
try {
return in.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
// Can't happen.
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/domain/TabsRecord.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/domain/TabsRecord.java
@@ -1,18 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.repositories.domain;
import java.util.ArrayList;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.db.Tab;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.NonArrayJSONException;
import org.mozilla.gecko.sync.Utils;
import android.content.ContentValues;
@@ -46,37 +47,38 @@ public class TabsRecord extends Record {
this(Utils.generateGuid(), COLLECTION_NAME, 0, false);
}
public String clientName;
public ArrayList<Tab> tabs;
@Override
public void initFromPayload(ExtendedJSONObject payload) {
- clientName = (String) payload.get("clientName");
+ clientName = payload.getString("clientName");
try {
tabs = tabsFrom(payload.getArray("tabs"));
} catch (NonArrayJSONException e) {
// Oh well.
tabs = new ArrayList<Tab>();
}
}
@SuppressWarnings("unchecked")
protected static JSONArray tabsToJSON(ArrayList<Tab> tabs) {
JSONArray out = new JSONArray();
for (Tab tab : tabs) {
- out.add(tabToJSONObject(tab));
+ out.put(tabToJSONObject(tab));
}
return out;
}
protected static ArrayList<Tab> tabsFrom(JSONArray in) {
- ArrayList<Tab> tabs = new ArrayList<Tab>(in.size());
- for (Object o : in) {
+ ArrayList<Tab> tabs = new ArrayList<>(in.length());
+ for (int i = 0; i < in.length(); i++) {
+ final Object o = Utils.getOrThrow(in, i);
if (o instanceof JSONObject) {
try {
tabs.add(TabsRecord.tabFromJSONObject((JSONObject) o));
} catch (NonArrayJSONException e) {
Logger.warn(LOG_TAG, "urlHistory is not an array for this tab.", e);
}
}
}
@@ -125,29 +127,32 @@ public class TabsRecord extends Record {
String title = obj.getString("title");
String icon = obj.getString("icon");
JSONArray history = obj.getArray("urlHistory");
// Last used is inexplicably a string in seconds. Most of the time.
long lastUsed = 0;
Object lU = obj.get("lastUsed");
if (lU instanceof Number) {
- lastUsed = ((Long) lU) * 1000L;
+ lastUsed = ((Number) lU).longValue() * 1000L;
} else if (lU instanceof String) {
try {
lastUsed = Long.parseLong((String) lU, 10) * 1000L;
} catch (NumberFormatException e) {
Logger.debug(TabsRecord.LOG_TAG, "Invalid number format in lastUsed: " + lU);
}
}
return new Tab(title, icon, history, lastUsed);
}
- @SuppressWarnings("unchecked")
- public static JSONObject tabToJSONObject(Tab tab) {
+ private static JSONObject tabToJSONObject(Tab tab) {
JSONObject o = new JSONObject();
- o.put("title", tab.title);
- o.put("icon", tab.icon);
- o.put("urlHistory", tab.history);
- o.put("lastUsed", tab.lastUsed / 1000);
+ try {
+ o.put("title", tab.title);
+ o.put("icon", tab.icon);
+ o.put("urlHistory", tab.history);
+ o.put("lastUsed", tab.lastUsed / 1000);
+ } catch (JSONException e) {
+ throw new IllegalStateException("'title', 'icon', 'urlHistory', 'lastUsed' are null?", e);
+ }
return o;
}
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/uploaders/BatchingUploader.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/uploaders/BatchingUploader.java
@@ -2,25 +2,23 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.repositories.uploaders;
import android.net.Uri;
import android.support.annotation.VisibleForTesting;
-import org.json.simple.JSONObject;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.CryptoRecord;
+import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.InfoConfiguration;
-import org.mozilla.gecko.sync.Server15PreviousPostFailedException;
import org.mozilla.gecko.sync.net.AuthHeaderProvider;
import org.mozilla.gecko.sync.repositories.RepositorySession;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionStoreDelegate;
-import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
import org.mozilla.gecko.sync.repositories.domain.Record;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicLong;
/**
* Uploader which implements batching introduced in Sync 1.5.
@@ -139,33 +137,34 @@ public class BatchingUploader {
public void process(final Record record) {
final String guid = record.guid;
// If store failed entirely, just bail out. We've already told our delegate that we failed.
if (payloadDispatcher.storeFailed.get() || aborted) {
return;
}
- final JSONObject recordJSON = record.toJSONObject();
+ final ExtendedJSONObject recordJSON = record.toJSONObject();
- final String payloadField = (String) recordJSON.get(CryptoRecord.KEY_PAYLOAD);
+ // TODO does JSONObject.get' throw when field is missing? Don't have source on an airplane :/
+ final String payloadField = recordJSON.getString(CryptoRecord.KEY_PAYLOAD);;
if (payloadField == null) {
failRecordStore(new IllegalRecordException(), record, false);
return;
}
// We can't upload individual records whose payload fields exceed our payload field byte limit.
// UTF-8 uses 1 byte per character for the ASCII range. Contents of the payloadField are
// base64 and hex encoded, so character count is sufficient.
if (payloadField.length() > this.maxPayloadFieldBytes) {
failRecordStore(new PayloadTooLargeToUpload(), record, true);
return;
}
- final byte[] recordBytes = Record.stringToJSONBytes(recordJSON.toJSONString());
+ final byte[] recordBytes = Record.stringToJSONBytes(recordJSON.toString());
if (recordBytes == null) {
failRecordStore(new IllegalRecordException(), record, false);
return;
}
final long recordDeltaByteCount = recordBytes.length + PER_RECORD_OVERHEAD_BYTE_COUNT;
Logger.debug(LOG_TAG, "Processing a record with guid: " + guid);
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/uploaders/PayloadUploadDelegate.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/uploaders/PayloadUploadDelegate.java
@@ -1,15 +1,15 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.repositories.uploaders;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.HTTPFailureException;
import org.mozilla.gecko.sync.NonArrayJSONException;
import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.Server15RecordPostFailedException;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.net.AuthHeaderProvider;
@@ -134,32 +134,32 @@ class PayloadUploadDelegate implements S
JSONArray success;
try {
success = body.getArray("success");
} catch (NonArrayJSONException e) {
handleRequestError(e);
return;
}
- if (success != null && !success.isEmpty()) {
+ if (success != null && success.length() != 0) {
Logger.trace(LOG_TAG, "Successful records: " + success.toString());
- dispatcher.batchWhiteboard.recordsSucceeded(success.size());
+ dispatcher.batchWhiteboard.recordsSucceeded(success.length());
}
// GC
success = null;
ExtendedJSONObject failed;
try {
failed = body.getObject("failed");
} catch (NonObjectJSONException e) {
handleRequestError(e);
return;
}
- if (failed != null && !failed.object.isEmpty()) {
+ if (failed != null && !failed.keySet().isEmpty()) {
Logger.debug(LOG_TAG, "Failed records: " + failed.object.toString());
for (String guid : failed.keySet()) {
dispatcher.recordFailed(guid);
}
dispatcher.payloadFailed(new Server15RecordPostFailedException());
return;
}
// GC
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/RecentHistoryServerSyncStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/RecentHistoryServerSyncStage.java
@@ -1,14 +1,15 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.stage;
+import org.json.JSONException;
import org.mozilla.gecko.sync.MetaGlobalException;
import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.SynchronizerConfiguration;
import org.mozilla.gecko.sync.repositories.ConfigurableServer15Repository;
import org.mozilla.gecko.sync.repositories.NonPersistentRepositoryStateProvider;
import org.mozilla.gecko.sync.repositories.Repository;
import org.mozilla.gecko.sync.repositories.RepositoryStateProvider;
import org.mozilla.gecko.sync.repositories.android.HistoryRepository;
@@ -106,15 +107,15 @@ public class RecentHistoryServerSyncStag
if (session.config == null) {
return false;
}
final SynchronizerConfiguration synchronizerConfiguration;
try {
synchronizerConfiguration = new SynchronizerConfiguration(session.config.getBranch(getCollection() + "."));
- } catch (IOException|NonObjectJSONException e) {
+ } catch (JSONException e) {
return false;
}
return synchronizerConfiguration.localBundle.getTimestamp() == -1;
}
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/ServerSyncStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/ServerSyncStage.java
@@ -2,16 +2,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.stage;
import android.content.Context;
import android.os.SystemClock;
+import org.json.JSONException;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.EngineSettings;
import org.mozilla.gecko.sync.GlobalSession;
import org.mozilla.gecko.sync.HTTPFailureException;
import org.mozilla.gecko.sync.MetaGlobalException;
import org.mozilla.gecko.sync.NoCollectionKeysSetException;
import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.ReflowIsNecessaryException;
@@ -80,17 +81,17 @@ public abstract class ServerSyncStage ex
*
* @return true if this stage should be executed.
* @throws MetaGlobalException
*/
protected boolean isEnabled() throws MetaGlobalException {
EngineSettings engineSettings = null;
try {
engineSettings = getEngineSettings();
- } catch (Exception e) {
+ } catch (JSONException e) {
Logger.warn(LOG_TAG, "Unable to get engine settings for " + this + ": fetching config failed.", e);
// Fall through; null engineSettings will pass below.
}
// We can be disabled by the server's meta/global record, or malformed in the server's meta/global record,
// or by the user manually in Sync Settings.
// We catch the subclasses of MetaGlobalException to trigger various resets and wipes in execute().
boolean enabledInMetaGlobal = session.isEngineRemotelyEnabled(this.getEngineName(), engineSettings);
@@ -132,17 +133,17 @@ public abstract class ServerSyncStage ex
if (enabledInMetaGlobal != enabledInSelection) {
// Engine enable state has been changed by the user.
Logger.debug(LOG_TAG, "Engine state has been changed by user. Throwing exception.");
throw new MetaGlobalException.MetaGlobalEngineStateChangedException(enabledInSelection);
}
}
}
- private EngineSettings getEngineSettings() throws NonObjectJSONException, IOException {
+ protected EngineSettings getEngineSettings() throws JSONException {
Integer version = getStorageVersion();
if (version == null) {
Logger.warn(LOG_TAG, "null storage version for " + this + "; using version 0.");
version = 0;
}
SynchronizerConfiguration config = this.getConfig();
if (config == null) {
@@ -213,25 +214,25 @@ public abstract class ServerSyncStage ex
protected String bundlePrefix() {
return this.getCollection() + ".";
}
/* package-private */ String statePreferencesPrefix() {
return this.getCollection() + ".state.";
}
- protected SynchronizerConfiguration getConfig() throws NonObjectJSONException, IOException {
+ protected SynchronizerConfiguration getConfig() throws JSONException {
return new SynchronizerConfiguration(session.config.getBranch(bundlePrefix()));
}
private void persistConfig(SynchronizerConfiguration synchronizerConfiguration) {
synchronizerConfiguration.persist(session.config.getBranch(bundlePrefix()));
}
- private Synchronizer getConfiguredSynchronizer() throws NoCollectionKeysSetException, URISyntaxException, NonObjectJSONException, IOException {
+ private Synchronizer getConfiguredSynchronizer() throws NoCollectionKeysSetException, URISyntaxException, JSONException {
Repository remote = wrappedServerRepo();
Synchronizer synchronizer = getSynchronizer();
synchronizer.repositoryA = remote;
synchronizer.repositoryB = this.getLocalRepository();
synchronizer.load(getConfig());
return synchronizer;
@@ -263,17 +264,17 @@ public abstract class ServerSyncStage ex
* Reset timestamps and possibly set syncID.
* @param syncID if non-null, new syncID to persist.
*/
private void resetLocalWithSyncID(String syncID) {
// Clear both timestamps.
SynchronizerConfiguration config;
try {
config = this.getConfig();
- } catch (Exception e) {
+ } catch (JSONException e) {
Logger.warn(LOG_TAG, "Unable to reset " + this + ": fetching config failed.", e);
return;
}
if (syncID != null) {
config.syncID = syncID;
Logger.info(LOG_TAG, "Setting syncID for " + this + " to '" + syncID + "'.");
}
@@ -586,17 +587,17 @@ public abstract class ServerSyncStage ex
try {
synchronizer = this.getConfiguredSynchronizer();
} catch (NoCollectionKeysSetException e) {
session.abort(e, "No CollectionKeys.");
return;
} catch (URISyntaxException e) {
session.abort(e, "Invalid URI syntax for server repository.");
return;
- } catch (NonObjectJSONException | IOException e) {
+ } catch (JSONException e) {
session.abort(e, "Invalid persisted JSON for config.");
return;
}
Logger.debug(LOG_TAG, "Invoking synchronizer.");
synchronizer.synchronize(session.getContext(), this);
Logger.debug(LOG_TAG, "Reached end of execute.");
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/SyncClientsEngineStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/SyncClientsEngineStage.java
@@ -3,31 +3,32 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.stage;
import android.accounts.Account;
import android.content.Context;
import android.os.SystemClock;
import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.Log;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONObject;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountClient;
import org.mozilla.gecko.background.fxa.FxAccountClient20;
import org.mozilla.gecko.background.fxa.FxAccountClientException;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.State;
@@ -57,20 +58,22 @@ import org.mozilla.gecko.sync.repositori
import org.mozilla.gecko.sync.telemetry.TelemetryCollector;
import ch.boye.httpclientandroidlib.HttpStatus;
public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
private static final String LOG_TAG = "SyncClientsEngineStage";
public static final String COLLECTION_NAME = "clients";
- public static final String STAGE_NAME = COLLECTION_NAME;
- public static final int CLIENTS_TTL_REFRESH = 604800000; // 7 days in milliseconds.
- public static final int MAX_UPLOAD_FAILURE_COUNT = 5;
- public static final long NOTIFY_TAB_SENT_TTL_SECS = TimeUnit.SECONDS.convert(1L, TimeUnit.HOURS); // 1 hour
+ /* package-private */ static final String STAGE_NAME = COLLECTION_NAME;
+ @VisibleForTesting
+ protected static final int CLIENTS_TTL_REFRESH = 604800000; // 7 days in milliseconds.
+ @VisibleForTesting
+ protected static final int MAX_UPLOAD_FAILURE_COUNT = 5;
+ private static final long NOTIFY_TAB_SENT_TTL_SECS = TimeUnit.SECONDS.convert(1L, TimeUnit.HOURS); // 1 hour
// Reasons behind sending collection_changed push notifications.
public static final String COLLECTION_MODIFIED_REASON_SENDTAB = "sendtab";
public static final String COLLECTION_MODIFIED_REASON_FIRSTSYNC = "firstsync";
protected final ClientRecordFactory factory = new ClientRecordFactory();
protected ClientUploadDelegate clientUploadDelegate;
protected ClientDownloadDelegate clientDownloadDelegate;
@@ -89,17 +92,17 @@ public class SyncClientsEngineStage exte
protected synchronized ClientsDatabaseAccessor getClientsDatabaseAccessor() {
if (db == null) {
db = new ClientsDatabaseAccessor(session.getContext());
}
return db;
}
- protected synchronized void closeDataAccessor() {
+ private synchronized void closeDataAccessor() {
if (db == null) {
return;
}
db.close();
db = null;
}
/**
@@ -240,55 +243,51 @@ public class SyncClientsEngineStage exte
@Override
public void handleSuccess(ExtendedJSONObject result) {
Log.i(LOG_TAG, "Devices notified");
}
});
}
@NonNull
- @SuppressWarnings("unchecked")
private ExtendedJSONObject createNotifyClientsBody(@NonNull List<String> devicesToNotify,
long ttl, @NonNull String reason) {
final ExtendedJSONObject body = new ExtendedJSONObject();
- final JSONArray to = new JSONArray();
- to.addAll(devicesToNotify);
+ final JSONArray to = new JSONArray(devicesToNotify);
body.put("to", to);
createNotifyClientsHelper(body, ttl, reason);
return body;
}
@NonNull
- @SuppressWarnings("unchecked")
private ExtendedJSONObject createNotifyAllClientsBody(@NonNull String localFxADeviceId,
long ttl, @NonNull String reason) {
final ExtendedJSONObject body = new ExtendedJSONObject();
body.put("to", "all");
final JSONArray excluded = new JSONArray();
- excluded.add(localFxADeviceId);
+ excluded.put(localFxADeviceId);
body.put("excluded", excluded);
createNotifyClientsHelper(body, ttl, reason);
return body;
}
private void createNotifyClientsHelper(ExtendedJSONObject body, long ttl,
@NonNull String reason) {
body.put("payload", createNotifyDevicesPayload(reason));
body.put("TTL", ttl);
}
@NonNull
- @SuppressWarnings("unchecked")
private ExtendedJSONObject createNotifyDevicesPayload(@NonNull String reason) {
final ExtendedJSONObject payload = new ExtendedJSONObject();
payload.put("version", 1);
payload.put("command", "sync:collection_changed");
final ExtendedJSONObject data = new ExtendedJSONObject();
final JSONArray collections = new JSONArray();
- collections.add("clients");
+ collections.put("clients");
data.put("collections", collections);
data.put("reason", reason);
payload.put("data", data);
return payload;
}
@Override
public void handleRequestFailure(SyncStorageResponse response) {
@@ -348,18 +347,18 @@ public class SyncClientsEngineStage exte
} catch (NoCollectionKeysSetException e) {
return null;
}
}
}
public class ClientUploadDelegate extends WBORequestDelegate {
protected static final String LOG_TAG = "ClientUploadDelegate";
- public Long currentlyUploadingRecordTimestamp;
- public boolean currentlyUploadingLocalRecord;
+ /* package-private */ Long currentlyUploadingRecordTimestamp;
+ /* package-private */ boolean currentlyUploadingLocalRecord;
@Override
public AuthHeaderProvider getAuthHeaderProvider() {
return session.getAuthHeaderProvider();
}
private void setUploadDetails(boolean isLocalRecord) {
// Use the timestamp for the whole collection per Sync storage 1.1 spec.
@@ -502,18 +501,18 @@ public class SyncClientsEngineStage exte
protected String getLocalClientVersion() {
return AppConstants.MOZ_APP_VERSION;
}
@SuppressWarnings("unchecked")
protected JSONArray getLocalClientProtocols() {
final JSONArray protocols = new JSONArray();
- protocols.add(ClientRecord.PROTOCOL_LEGACY_SYNC);
- protocols.add(ClientRecord.PROTOCOL_FXA_SYNC);
+ protocols.put(ClientRecord.PROTOCOL_LEGACY_SYNC);
+ protocols.put(ClientRecord.PROTOCOL_FXA_SYNC);
return protocols;
}
protected ClientRecord newLocalClientRecord(ClientsDataDelegate delegate) {
final String ourGUID = delegate.getAccountGUID();
final String ourName = delegate.getClientName();
ClientRecord r = new ClientRecord(ourGUID);
@@ -536,17 +535,17 @@ public class SyncClientsEngineStage exte
r.fxaDeviceId = deviceId;
}
}
return r;
}
// TODO: Bug 726055 - More considered handling of when to sync.
- protected boolean shouldDownload() {
+ private boolean shouldDownload() {
// Ask info/collections whether a download is needed.
return true;
}
protected boolean shouldUpload() {
if (shouldUploadLocalRecord) {
return true;
}
@@ -576,25 +575,26 @@ public class SyncClientsEngineStage exte
!getLocalClientProtocols().equals(r.protocols)) {
shouldUploadLocalRecord = true;
}
processCommands(r.commands);
}
protected void processCommands(JSONArray commands) {
if (commands == null ||
- commands.size() == 0) {
+ commands.length() == 0) {
return;
}
shouldUploadLocalRecord = true;
CommandProcessor processor = CommandProcessor.getProcessor();
- for (Object o : commands) {
- processor.processCommand(session, new ExtendedJSONObject((JSONObject) o));
+ for (int i = 0; i < commands.length(); i++) {
+ final JSONObject o = (JSONObject) Utils.getOrThrow(commands, i);
+ processor.processCommand(session, new ExtendedJSONObject(o));
}
}
@SuppressWarnings("unchecked")
protected void addCommands(ClientRecord record) throws NullCursorException {
Logger.trace(LOG_TAG, "Adding commands to " + record.guid);
List<Command> commands = db.fetchCommandsForClient(record.guid);
@@ -603,17 +603,17 @@ public class SyncClientsEngineStage exte
return;
}
for (Command command : commands) {
JSONObject jsonCommand = command.asJSONObject();
if (record.commands == null) {
record.commands = new JSONArray();
}
- record.commands.add(jsonCommand);
+ record.commands.put(jsonCommand);
}
modifiedClientsToUpload.add(record);
}
@SuppressWarnings("unchecked")
protected void uploadRemoteRecords() {
Logger.trace(LOG_TAG, "In uploadRemoteRecords. Uploading " + modifiedClientsToUpload.size() + " records" );
@@ -633,19 +633,19 @@ public class SyncClientsEngineStage exte
return;
}
JSONArray cryptoRecords = new JSONArray();
for (ClientRecord record : modifiedClientsToUpload) {
Logger.trace(LOG_TAG, "Record " + record.guid + " is being uploaded" );
CryptoRecord cryptoRecord = encryptClientRecord(record);
- cryptoRecords.add(cryptoRecord.toJSONObject());
+ cryptoRecords.put(cryptoRecord.toJSONObject());
}
- Logger.debug(LOG_TAG, "Uploading records: " + cryptoRecords.size());
+ Logger.debug(LOG_TAG, "Uploading records: " + cryptoRecords.length());
clientUploadDelegate.setUploadDetails(false);
this.uploadClientRecords(cryptoRecords);
}
protected void checkAndUpload() {
if (!shouldUpload()) {
Logger.debug(LOG_TAG, "Not uploading client record.");
doAdvance();
@@ -655,17 +655,17 @@ public class SyncClientsEngineStage exte
final ClientRecord localClient = newLocalClientRecord(session.getClientsDelegate());
clientUploadDelegate.setUploadDetails(true);
CryptoRecord cryptoRecord = encryptClientRecord(localClient);
if (cryptoRecord != null) {
this.uploadClientRecord(cryptoRecord);
}
}
- protected CryptoRecord encryptClientRecord(ClientRecord recordToUpload) {
+ private CryptoRecord encryptClientRecord(ClientRecord recordToUpload) {
// Generate CryptoRecord from ClientRecord to upload.
final String encryptionFailure = "Couldn't encrypt new client record.";
try {
CryptoRecord cryptoRecord = recordToUpload.getEnvelope();
cryptoRecord.keyBundle = clientUploadDelegate.keyBundle();
if (cryptoRecord.keyBundle == null) {
doAbort(new NoCollectionKeysSetException(), "No collection keys set.");
@@ -675,17 +675,17 @@ public class SyncClientsEngineStage exte
} catch (UnsupportedEncodingException e) {
doAbort(e, encryptionFailure + " Unsupported encoding.");
} catch (CryptoException e) {
doAbort(e, encryptionFailure);
}
return null;
}
- public void clearRecordsToUpload() {
+ private void clearRecordsToUpload() {
try {
getClientsDatabaseAccessor().wipeCommandsTable();
modifiedClientsToUpload.clear();
} finally {
closeDataAccessor();
}
}
@@ -701,17 +701,17 @@ public class SyncClientsEngineStage exte
Logger.trace(LOG_TAG, "Downloading client records.");
request.get();
} catch (URISyntaxException e) {
doAbort(e, "Invalid URI.");
}
}
protected void uploadClientRecords(JSONArray records) {
- Logger.trace(LOG_TAG, "Uploading " + records.size() + " client records.");
+ Logger.trace(LOG_TAG, "Uploading " + records.length() + " client records.");
try {
final URI postURI = session.config.collectionURI(COLLECTION_NAME, false);
final SyncStorageRecordRequest request = new SyncStorageRecordRequest(postURI);
request.delegate = clientUploadDelegate;
request.post(records);
} catch (URISyntaxException e) {
doAbort(e, "Invalid URI.");
} catch (Exception e) {
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/telemetry/TelemetryCollector.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/telemetry/TelemetryCollector.java
@@ -148,17 +148,17 @@ public class TelemetryCollector {
final long took = this.finished - this.started;
final Bundle telemetry = new Bundle();
telemetry.putString(TelemetryContract.KEY_LOCAL_UID, this.hashedUID);
telemetry.putString(TelemetryContract.KEY_LOCAL_DEVICE_ID, this.hashedDeviceID);
telemetry.putParcelableArrayList(TelemetryContract.KEY_DEVICES, this.devices);
telemetry.putLong(TelemetryContract.KEY_TOOK, took);
if (this.error != null) {
- telemetry.putSerializable(TelemetryContract.KEY_ERROR, this.error.object);
+ telemetry.putString(TelemetryContract.KEY_ERROR, this.error.object.toString());
}
telemetry.putSerializable(TelemetryContract.KEY_STAGES, this.stageCollectors);
if (this.didRestart) {
telemetry.putBoolean(TelemetryContract.KEY_RESTARTED, true);
}
return telemetry;
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/validation/BookmarkValidator.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/validation/BookmarkValidator.java
@@ -1,15 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.validation;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
+import org.json.JSONException;
import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -33,18 +34,25 @@ public class BookmarkValidator {
}
private void checkServerFolder(BookmarkRecord r, BookmarkValidationResults results) {
if (r.children == null) {
return;
}
HashSet<String> seenChildGUIDs = new HashSet<>();
- for (int i = 0; i < r.children.size(); ++i) {
- String childGUID = (String) r.children.get(i);
+ for (int i = 0; i < r.children.length(); ++i) {
+ String childGUID;
+ try {
+ childGUID = (String) r.children.get(i);
+ } catch (JSONException e) {
+ // This shouldn't happen.
+ // Crash if it does; elements shouldn't just disappear from the array.
+ throw new IllegalStateException("Couldn't read element from server folder children array during validation.");
+ }
if (seenChildGUIDs.contains(childGUID)) {
results.duplicateChildren.add(
new BookmarkValidationResults.ParentChildPair(r.guid, childGUID)
);
continue;
}
seenChildGUIDs.add(childGUID);
@@ -105,18 +113,25 @@ public class BookmarkValidator {
new BookmarkValidationResults.ParentChildPair(parentID, record.guid)
);
} else if (!listedParent.isFolder()) {
results.parentNotFolder.add(
new BookmarkValidationResults.ParentChildPair(parentID, record.guid)
);
} else {
boolean foundChild = false;
- for (int i = 0; i < listedParent.children.size() && !foundChild; ++i) {
- String childGUID = (String) listedParent.children.get(i);
+ for (int i = 0; i < listedParent.children.length() && !foundChild; ++i) {
+ String childGUID;
+ try {
+ childGUID = (String) listedParent.children.get(i);
+ } catch (JSONException e) {
+ // This shouldn't happen.
+ // Crash if it does; elements shouldn't just disappear from the array.
+ throw new IllegalStateException("Couldn't read element from listedParent.children array during validation.");
+ }
foundChild = childGUID.equals(record.guid);
}
if (!foundChild) {
results.parentChildMismatches.add(
new BookmarkValidationResults.ParentChildPair(parentID, record.guid)
);
}
}
@@ -176,20 +191,32 @@ public class BookmarkValidator {
boolean sawDiff = false;
if (!p.client.parentID.equals(p.server.parentID)) {
results.structuralDifferenceParentIDs.add(p.client.guid);
// Just record we saw it, since we still want to check the childGUIDs
sawDiff = true;
}
if (p.client.children != null && p.server.children != null) {
- if (p.client.children.size() == p.server.children.size()) {
- for (int i = 0; i < p.client.children.size(); ++i) {
- String clientChildGUID = (String) p.client.children.get(i);
- String serverChildGUID = (String) p.server.children.get(i);
+ if (p.client.children.length() == p.server.children.length()) {
+ for (int i = 0; i < p.client.children.length(); ++i) {
+ // These shouldn't throw.
+ // Crash if they do; elements shouldn't just disappear from the array.
+ String clientChildGUID;
+ String serverChildGUID;
+ try {
+ clientChildGUID = (String) p.client.children.get(i);
+ } catch (JSONException e) {
+ throw new IllegalStateException("Couldn't read element from p.client.children array during validation.");
+ }
+ try {
+ serverChildGUID = (String) p.server.children.get(i);
+ } catch (JSONException e) {
+ throw new IllegalStateException("Couldn't read element from p.server.children array during validation.");
+ }
if (!clientChildGUID.equals(serverChildGUID)) {
results.structuralDifferenceChildGUIDs.add(p.client.guid);
sawDiff = true;
break;
}
}
} else {
// They have different sizes, so the contents must be different.
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/validation/ValidationResults.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/validation/ValidationResults.java
@@ -1,39 +1,38 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.validation;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.mozilla.gecko.sync.Utils;
-import java.util.List;
import java.util.Map;
public abstract class ValidationResults {
/**
* Get the problems found by the validator. Must not contain numbers less than or equal to zero.
* Must use the same names for problems as other platforms!
*/
public abstract Map<String, Integer> summarizeResults();
/**
* Get the summary as JSON suitable for including in telemetry
*/
- @SuppressWarnings("unchecked")
public JSONArray jsonSummary() {
Map<String, Integer> problems = summarizeResults();
JSONArray result = new JSONArray();
for (Map.Entry<String, Integer> problem : problems.entrySet()) {
JSONObject o = new JSONObject();
- o.put("name", problem.getKey());
- o.put("count", problem.getValue());
- result.add(o);
+ Utils.putOrThrow(o, "name", problem.getKey());
+ Utils.putOrThrow(o, "count", problem.getValue());
+ result.put(o);
}
return result;
}
public boolean anyProblemsExist() {
return summarizeResults().size() > 0;
}
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/tokenserver/TokenServerClient.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/tokenserver/TokenServerClient.java
@@ -6,17 +6,19 @@ package org.mozilla.gecko.tokenserver;
import java.io.IOException;
import java.net.URI;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.SkewHandler;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.NonArrayJSONException;
import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.UnexpectedJSONException.BadRequiredFieldJSONException;
import org.mozilla.gecko.sync.net.AuthHeaderProvider;
import org.mozilla.gecko.sync.net.BaseResource;
@@ -158,25 +160,31 @@ public class TokenServerClient {
// The service shouldn't have any 3xx, so we don't need to handle those.
if (res.getStatusCode() != 200) {
// We should have a (Cornice) error report in the JSON. We log that to
// help with debugging.
List<ExtendedJSONObject> errorList = new ArrayList<ExtendedJSONObject>();
if (result.containsKey(JSON_KEY_ERRORS)) {
try {
- for (Object error : result.getArray(JSON_KEY_ERRORS)) {
+ final JSONArray errors = result.getArray(JSON_KEY_ERRORS);
+ // Should not happen, since we just checked for key presence above.
+ if (errors == null) {
+ throw new IllegalStateException("Failed to read '" + JSON_KEY_ERRORS + "' key from token response.");
+ }
+ for (int i = 0; i < errors.length(); i++) {
+ final Object error = errors.get(i);
Logger.warn(LOG_TAG, "" + error);
if (error instanceof JSONObject) {
errorList.add(new ExtendedJSONObject((JSONObject) error));
}
}
- } catch (NonArrayJSONException e) {
- Logger.warn(LOG_TAG, "Got non-JSON array '" + JSON_KEY_ERRORS + "'.", e);
+ } catch (NonArrayJSONException | JSONException e) {
+ Logger.warn(LOG_TAG, "Got an improper JSON array '" + JSON_KEY_ERRORS + "'.", e);
}
}
if (statusCode == 400) {
throw new TokenServerMalformedRequestException(errorList, result.toJSONString());
}
if (statusCode == 401) {
@@ -216,17 +224,17 @@ public class TokenServerClient {
}
// We shouldn't ever get here...
throw new TokenServerException(errorList);
}
try {
result.throwIfFieldsMissingOrMisTyped(new String[] { JSON_KEY_ID, JSON_KEY_KEY, JSON_KEY_API_ENDPOINT, JSON_KEY_HASHED_FXA_UID }, String.class);
- result.throwIfFieldsMissingOrMisTyped(new String[] { JSON_KEY_UID }, Long.class);
+ result.throwIfFieldsMissingOrMisTyped(new String[] { JSON_KEY_UID }, Integer.class);
} catch (BadRequiredFieldJSONException e ) {
throw new TokenServerMalformedResponseException(null, e);
}
Logger.debug(LOG_TAG, "Successful token response: " + result.getString(JSON_KEY_ID));
return new TokenServerToken(result.getString(JSON_KEY_ID),
result.getString(JSON_KEY_KEY),
--- a/mobile/android/services/src/test/java/org/mozilla/android/sync/net/test/TestClientsEngineStage.java
+++ b/mobile/android/services/src/test/java/org/mozilla/android/sync/net/test/TestClientsEngineStage.java
@@ -1,16 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.android.sync.net.test;
import ch.boye.httpclientandroidlib.HttpStatus;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
import org.mozilla.android.sync.test.helpers.MockGlobalSessionCallback;
import org.mozilla.android.sync.test.helpers.MockServer;
import org.mozilla.android.sync.test.helpers.MockSyncClientsEngineStage;
import org.mozilla.gecko.background.common.log.Logger;
@@ -293,31 +294,31 @@ public class TestClientsEngineStage exte
public void handleRequestError(Exception ex) {
super.handleRequestError(ex);
fail("Should not fail.");
}
}
public class UploadMockServer extends MockServer {
@SuppressWarnings("unchecked")
- private String postBodyForRecord(ClientRecord cr) {
+ private String postBodyForRecord(ClientRecord cr) throws JSONException {
final long now = cr.lastModified;
final BigDecimal modified = Utils.millisecondsToDecimalSeconds(now);
Logger.debug(LOG_TAG, "Now is " + now + " (" + modified + ")");
final JSONArray idArray = new JSONArray();
- idArray.add(cr.guid);
+ idArray.put(cr.guid);
final JSONObject result = new JSONObject();
result.put("modified", modified);
result.put("success", idArray);
result.put("failed", new JSONObject());
uploadBodyTimestamp = modified.toString();
- return result.toJSONString();
+ return result.toString();
}
private String putBodyForRecord(ClientRecord cr) {
final String modified = Utils.millisecondsToDecimalSecondsString(cr.lastModified);
uploadBodyTimestamp = modified;
return modified;
}
@@ -346,17 +347,17 @@ public class TestClientsEngineStage exte
Logger.debug(LOG_TAG, "Handling POST: " + request.getPath());
String content = request.getContent();
Logger.debug(LOG_TAG, "Content is " + content);
JSONArray array = ExtendedJSONObject.parseJSONArray(content);
Logger.debug(LOG_TAG, "Content is " + array);
KeyBundle keyBundle = session.keyBundleForCollection(COLLECTION_NAME);
- if (array.size() != 1) {
+ if (array.length() != 1) {
Logger.debug(LOG_TAG, "Expecting only one record! Fail!");
PrintStream bodyStream = this.handleBasicHeaders(request, response, 400, "text/plain");
bodyStream.println("Expecting only one record! Fail!");
bodyStream.close();
return;
}
CryptoRecord r = CryptoRecord.fromJSONRecord(new ExtendedJSONObject((JSONObject) array.get(0)));
@@ -421,23 +422,23 @@ public class TestClientsEngineStage exte
public void handle(Request request, Response response) {
try {
PrintStream bodyStream = this.handleBasicHeaders(request, response, 200, "application/newlines");
ClientRecord record = new ClientRecord(session.getClientsDelegate().getAccountGUID());
// Timestamp on server is 10 seconds after local timestamp
// (would trigger 412 if upload was attempted).
CryptoRecord cryptoRecord = cryptoFromClient(record);
- JSONObject object = cryptoRecord.toJSONObject();
+ JSONObject object = cryptoRecord.toJSONObject().object;
final long modified = (setRecentClientRecordTimestamp() + 10000) / 1000;
Logger.debug(LOG_TAG, "Setting modified to " + modified);
object.put("modified", modified);
- bodyStream.print(object.toJSONString() + "\n");
+ bodyStream.print(object.toString() + "\n");
bodyStream.close();
- } catch (IOException e) {
+ } catch (JSONException | IOException e) {
fail("Error handling downloaded client records in DownloadLocalRecordMockServer.");
}
}
}
private CryptoRecord cryptoFromClient(ClientRecord record) {
CryptoRecord cryptoRecord = record.getEnvelope();
cryptoRecord.keyBundle = clientDownloadDelegate.keyBundle();
@@ -506,17 +507,17 @@ public class TestClientsEngineStage exte
// If the protocol list is missing or wrong, we should reupload.
outdatedRecord.protocols = new JSONArray();
handleDownloadedLocalRecord(outdatedRecord);
assertTrue(shouldUpload());
shouldUploadLocalRecord = false;
assertFalse(shouldUpload());
- outdatedRecord.protocols.add("1.0");
+ outdatedRecord.protocols.put("1.0");
handleDownloadedLocalRecord(outdatedRecord);
assertTrue(shouldUpload());
}
@SuppressWarnings("unchecked")
@Test
public void testShouldUploadProcessCommands() throws NullCursorException {
// shouldUpload() returns false since array is size 0 and
@@ -524,20 +525,20 @@ public class TestClientsEngineStage exte
processCommands(new JSONArray());
setRecentClientRecordTimestamp();
assertFalse(shouldUploadLocalRecord);
assertFalse(shouldUpload());
// shouldUpload() returns true since array is size 1 even though
// it has not been long enough yet to require an upload.
JSONArray commands = new JSONArray();
- commands.add(new JSONObject());
+ commands.put(new JSONObject());
processCommands(commands);
setRecentClientRecordTimestamp();
- assertEquals(1, commands.size());
+ assertEquals(1, commands.length());
assertTrue(shouldUploadLocalRecord);
assertTrue(shouldUpload());
}
@Test
public void testWipeAndStoreShouldNotWipe() {
assertFalse(shouldWipe);
wipeAndStore(new ClientRecord());
@@ -767,34 +768,34 @@ public class TestClientsEngineStage exte
final ClientRecord remoteRecord = new ClientRecord();
remoteRecord.version = null;
final String expectedGUID = remoteRecord.guid;
this.addCommands(remoteRecord);
assertEquals(1, modifiedClientsToUpload.size());
final ClientRecord recordToUpload = modifiedClientsToUpload.get(0);
- assertEquals(4, recordToUpload.commands.size());
+ assertEquals(4, recordToUpload.commands.length());
assertEquals(expectedGUID, recordToUpload.guid);
assertEquals(null, recordToUpload.version);
}
@Test
public void testAddCommandsToVersionedClient() throws NullCursorException {
db = new TestAddCommandsMockClientsDatabaseAccessor();
final ClientRecord remoteRecord = new ClientRecord();
remoteRecord.version = "12a1";
final String expectedGUID = remoteRecord.guid;
this.addCommands(remoteRecord);
assertEquals(1, modifiedClientsToUpload.size());
final ClientRecord recordToUpload = modifiedClientsToUpload.get(0);
- assertEquals(4, recordToUpload.commands.size());
+ assertEquals(4, recordToUpload.commands.length());
assertEquals(expectedGUID, recordToUpload.guid);
assertEquals("12a1", recordToUpload.version);
}
@Test
public void testLastModifiedTimestamp() throws NullCursorException {
// If we uploaded a record a moment ago, we shouldn't upload another.
final long now = System.currentTimeMillis() - 1;
--- a/mobile/android/services/src/test/java/org/mozilla/android/sync/net/test/TestCredentialsEndToEnd.java
+++ b/mobile/android/services/src/test/java/org/mozilla/android/sync/net/test/TestCredentialsEndToEnd.java
@@ -1,18 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.android.sync.net.test;
+import org.json.JSONException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.ExtendedJSONObject;
-import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import ch.boye.httpclientandroidlib.Header;
@@ -41,17 +41,17 @@ public class TestCredentialsEndToEnd {
@Test
public void testUTF8() throws UnsupportedEncodingException {
final String in = "pïgéons1";
final String out = "pïgéons1";
assertEquals(out, Utils.decodeUTF8(in));
}
@Test
- public void testAuthHeaderFromPassword() throws NonObjectJSONException, IOException {
+ public void testAuthHeaderFromPassword() throws JSONException, IOException {
final ExtendedJSONObject parsed = new ExtendedJSONObject(DESKTOP_PASSWORD_JSON);
final String password = parsed.getString("password");
final String decoded = Utils.decodeUTF8(password);
final byte[] expectedBytes = Utils.decodeBase64(BTOA_PASSWORD);
final String expected = new String(expectedBytes, "UTF-8");
--- a/mobile/android/services/src/test/java/org/mozilla/android/sync/net/test/TestMetaGlobal.java
+++ b/mobile/android/services/src/test/java/org/mozilla/android/sync/net/test/TestMetaGlobal.java
@@ -1,24 +1,24 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.android.sync.net.test;
+import org.json.JSONException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
import org.mozilla.android.sync.test.helpers.MockServer;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.background.testhelpers.WaitHelper;
import org.mozilla.gecko.sync.CryptoRecord;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.MetaGlobal;
-import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.delegates.MetaGlobalDelegate;
import org.mozilla.gecko.sync.net.BaseResource;
import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider;
import org.mozilla.gecko.sync.net.SyncStorageResponse;
import org.simpleframework.http.Request;
import org.simpleframework.http.Response;
import java.util.HashSet;
@@ -216,17 +216,17 @@ public class TestMetaGlobal {
MockServer existingMetaGlobalServer = new MockServer(200, TEST_META_GLOBAL_MALFORMED_PAYLOAD_RESPONSE);
data.startHTTPServer(existingMetaGlobalServer);
final MockMetaGlobalFetchDelegate delegate = doFetch(global);
data.stopHTTPServer();
assertTrue(delegate.errorCalled);
assertNotNull(delegate.errorException);
- assertEquals(NonObjectJSONException.class, delegate.errorException.getClass());
+ assertEquals(JSONException.class, delegate.errorException.getClass());
}
@SuppressWarnings("static-method")
@Test
public void testSetFromRecord() throws Exception {
MetaGlobal mg = new MetaGlobal(null, null);
mg.setFromRecord(CryptoRecord.fromJSONRecord(TEST_META_GLOBAL_RESPONSE));
assertEquals("zPSQTm7WBVWB", mg.getSyncID());
--- a/mobile/android/services/src/test/java/org/mozilla/android/sync/net/test/TestSyncStorageRequest.java
+++ b/mobile/android/services/src/test/java/org/mozilla/android/sync/net/test/TestSyncStorageRequest.java
@@ -1,14 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.android.sync.net.test;
-import org.json.simple.JSONObject;
+import org.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.android.sync.test.helpers.BaseTestStorageRequestDelegate;
import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
import org.mozilla.android.sync.test.helpers.MockServer;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.net.AuthHeaderProvider;
import org.mozilla.gecko.sync.net.BaseResource;
@@ -18,17 +18,16 @@ import org.mozilla.gecko.sync.net.SyncSt
import org.simpleframework.http.Request;
import org.simpleframework.http.Response;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(TestRunner.class)
public class TestSyncStorageRequest {
private static final int TEST_PORT = HTTPServerTestHelper.getTestPort();
private static final String TEST_SERVER = "http://localhost:" + TEST_PORT;
--- a/mobile/android/services/src/test/java/org/mozilla/android/sync/test/TestCollectionKeys.java
+++ b/mobile/android/services/src/test/java/org/mozilla/android/sync/test/TestCollectionKeys.java
@@ -1,14 +1,15 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.android.sync.test;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
+import org.json.JSONException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.apache.commons.codec.binary.Base64;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.CollectionKeys;
import org.mozilla.gecko.sync.CryptoRecord;
import org.mozilla.gecko.sync.NoCollectionKeysSetException;
import org.mozilla.gecko.sync.NonObjectJSONException;
@@ -65,49 +66,49 @@ public class TestCollectionKeys {
}
public static void assertSame(byte[] arrayOne, byte[] arrayTwo) {
assertTrue(Arrays.equals(arrayOne, arrayTwo));
}
@Test
- public void testSetKeysFromWBO() throws IOException, NonObjectJSONException, CryptoException, NoCollectionKeysSetException {
+ public void testSetKeysFromWBO() throws IOException, JSONException, NonObjectJSONException, CryptoException, NoCollectionKeysSetException {
String json = "{\"default\":[\"3fI6k1exImMgAKjilmMaAWxGqEIzFX/9K5EjEgH99vc=\",\"/AMaoCX4hzic28WY94XtokNi7N4T0nv+moS1y5wlbug=\"],\"collections\":{},\"collection\":\"crypto\",\"id\":\"keys\"}";
CryptoRecord rec = new CryptoRecord(json);
KeyBundle syncKeyBundle = new KeyBundle("slyjcrjednxd6rf4cr63vqilmkus6zbe", "6m8mv8ex2brqnrmsb9fjuvfg7y");
rec.keyBundle = syncKeyBundle;
rec.encrypt();
CollectionKeys ck = new CollectionKeys();
ck.setKeyPairsFromWBO(rec, syncKeyBundle);
byte[] input = "3fI6k1exImMgAKjilmMaAWxGqEIzFX/9K5EjEgH99vc=".getBytes("UTF-8");
byte[] expected = Base64.decodeBase64(input);
assertSame(expected, ck.defaultKeyBundle().getEncryptionKey());
}
@Test
- public void testCryptoRecordFromCollectionKeys() throws CryptoException, NoCollectionKeysSetException, IOException, NonObjectJSONException {
+ public void testCryptoRecordFromCollectionKeys() throws CryptoException, NoCollectionKeysSetException, IOException, JSONException, NonObjectJSONException {
CollectionKeys ck1 = CollectionKeys.generateCollectionKeys();
assertNotNull(ck1.defaultKeyBundle());
assertEquals(ck1.keyBundleForCollection("foobar"), ck1.defaultKeyBundle());
CryptoRecord rec = ck1.asCryptoRecord();
assertEquals(rec.collection, "crypto");
assertEquals(rec.guid, "keys");
JSONArray defaultKey = (JSONArray) rec.payload.get("default");
assertSame(Base64.decodeBase64((String) (defaultKey.get(0))), ck1.defaultKeyBundle().getEncryptionKey());
CollectionKeys ck2 = new CollectionKeys();
ck2.setKeyPairsFromWBO(rec, null);
assertSame(ck1.defaultKeyBundle().getEncryptionKey(), ck2.defaultKeyBundle().getEncryptionKey());
}
@Test
- public void testCreateKeysBundle() throws CryptoException, NonObjectJSONException, IOException, NoCollectionKeysSetException {
+ public void testCreateKeysBundle() throws CryptoException, JSONException, NonObjectJSONException, IOException, NoCollectionKeysSetException {
String username = "b6evr62dptbxz7fvebek7btljyu322wp";
String friendlyBase32SyncKey = "basuxv2426eqj7frhvpcwkavdi";
KeyBundle syncKeyBundle = new KeyBundle(username, friendlyBase32SyncKey);
CollectionKeys ck = CollectionKeys.generateCollectionKeys();
CryptoRecord unencrypted = ck.asCryptoRecord();
unencrypted.keyBundle = syncKeyBundle;
--- a/mobile/android/services/src/test/java/org/mozilla/android/sync/test/TestCommandProcessor.java
+++ b/mobile/android/services/src/test/java/org/mozilla/android/sync/test/TestCommandProcessor.java
@@ -1,13 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.android.sync.test;
+import org.json.JSONException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.CommandProcessor;
import org.mozilla.gecko.sync.CommandRunner;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.GlobalSession;
import org.mozilla.gecko.sync.NonObjectJSONException;
@@ -50,67 +51,67 @@ public class TestCommandProcessor extend
@Test
public void testRegisterCommand() throws NonObjectJSONException, IOException {
assertNull(commands.get(commandType));
this.registerCommand(commandType, new MockCommandRunner(1));
assertNotNull(commands.get(commandType));
}
@Test
- public void testProcessRegisteredCommand() throws NonObjectJSONException, IOException {
+ public void testProcessRegisteredCommand() throws JSONException, IOException {
commandExecuted = false;
ExtendedJSONObject unparsedCommand = new ExtendedJSONObject(wellFormedCommand);
this.registerCommand(commandType, new MockCommandRunner(1));
this.processCommand(session, unparsedCommand);
assertTrue(commandExecuted);
}
@Test
- public void testProcessUnregisteredCommand() throws NonObjectJSONException, IOException {
+ public void testProcessUnregisteredCommand() throws JSONException, IOException {
commandExecuted = false;
ExtendedJSONObject unparsedCommand = new ExtendedJSONObject(wellFormedCommand);
this.processCommand(session, unparsedCommand);
assertFalse(commandExecuted);
}
@Test
- public void testProcessInvalidCommand() throws NonObjectJSONException, IOException {
+ public void testProcessInvalidCommand() throws JSONException, IOException {
ExtendedJSONObject unparsedCommand = new ExtendedJSONObject(commandWithNoType);
this.registerCommand(commandType, new MockCommandRunner(1));
this.processCommand(session, unparsedCommand);
assertFalse(commandExecuted);
}
@Test
- public void testParseCommandNoType() throws NonObjectJSONException, IOException {
+ public void testParseCommandNoType() throws JSONException, IOException {
ExtendedJSONObject unparsedCommand = new ExtendedJSONObject(commandWithNoType);
assertNull(CommandProcessor.parseCommand(unparsedCommand));
}
@Test
- public void testParseCommandNoArgs() throws NonObjectJSONException, IOException {
+ public void testParseCommandNoArgs() throws JSONException, IOException {
ExtendedJSONObject unparsedCommand = new ExtendedJSONObject(commandWithNoArgs);
assertNull(CommandProcessor.parseCommand(unparsedCommand));
}
@Test
- public void testParseWellFormedCommand() throws NonObjectJSONException, IOException {
+ public void testParseWellFormedCommand() throws JSONException, IOException {
ExtendedJSONObject unparsedCommand = new ExtendedJSONObject(wellFormedCommand);
Command parsedCommand = CommandProcessor.parseCommand(unparsedCommand);
assertNotNull(parsedCommand);
- assertEquals(2, parsedCommand.args.size());
+ assertEquals(2, parsedCommand.args.length());
assertEquals(commandType, parsedCommand.commandType);
}
@Test
- public void testParseCommandNullArg() throws NonObjectJSONException, IOException {
+ public void testParseCommandNullArg() throws JSONException, IOException {
ExtendedJSONObject unparsedCommand = new ExtendedJSONObject(wellFormedCommandWithNullArgs);
Command parsedCommand = CommandProcessor.parseCommand(unparsedCommand);
assertNotNull(parsedCommand);
- assertEquals(4, parsedCommand.args.size());
+ assertEquals(4, parsedCommand.args.length());
assertEquals(commandType, parsedCommand.commandType);
final List<String> expectedArgs = new ArrayList<String>();
expectedArgs.add("https://bugzilla.mozilla.org/show_bug.cgi?id=731341");
expectedArgs.add(null);
expectedArgs.add("PKsljsuqYbGg");
expectedArgs.add(null);
assertEquals(expectedArgs, parsedCommand.getArgsList());
}
--- a/mobile/android/services/src/test/java/org/mozilla/android/sync/test/TestCryptoRecord.java
+++ b/mobile/android/services/src/test/java/org/mozilla/android/sync/test/TestCryptoRecord.java
@@ -1,22 +1,22 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.android.sync.test;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.apache.commons.codec.binary.Base64;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.CryptoRecord;
import org.mozilla.gecko.sync.ExtendedJSONObject;
-import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.crypto.CryptoException;
import org.mozilla.gecko.sync.crypto.KeyBundle;
import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
import org.mozilla.gecko.sync.repositories.domain.HistoryRecord;
import org.mozilla.gecko.sync.repositories.domain.Record;
import java.io.IOException;
@@ -29,17 +29,17 @@ import static org.junit.Assert.assertNul
import static org.junit.Assert.assertTrue;
@RunWith(TestRunner.class)
public class TestCryptoRecord {
String base64EncryptionKey = "9K/wLdXdw+nrTtXo4ZpECyHFNr4d7aYHqeg3KW9+m6Q=";
String base64HmacKey = "MMntEfutgLTc8FlTLQFms8/xMPmCldqPlq/QQXEjx70=";
@Test
- public void testBaseCryptoRecordEncrypt() throws IOException, NonObjectJSONException, CryptoException {
+ public void testBaseCryptoRecordEncrypt() throws IOException, JSONException, CryptoException {
ExtendedJSONObject clearPayload = new ExtendedJSONObject("{\"id\":\"5qRsgXWRJZXr\"," +
"\"title\":\"Index of file:///Users/jason/Library/Application " +
"Support/Firefox/Profiles/ksgd7wpk.LocalSyncServer/weave/logs/\"," +
"\"histUri\":\"file:///Users/jason/Library/Application%20Support/Firefox/Profiles" +
"/ksgd7wpk.LocalSyncServer/weave/logs/\",\"visits\":[{\"type\":1," +
"\"date\":1319149012372425}]}");
@@ -253,34 +253,37 @@ public class TestCryptoRecord {
encrypted.keyBundle = syncKeyBundle;
CryptoRecord decrypted = encrypted.decrypt();
// We don't necessarily produce exactly the same JSON but we do have the same values.
ExtendedJSONObject expectedJson = new ExtendedJSONObject(expectedDecryptedText);
assertEquals(expectedJson.get("id"), decrypted.payload.get("id"));
assertEquals(expectedJson.get("default"), decrypted.payload.get("default"));
assertEquals(expectedJson.get("collection"), decrypted.payload.get("collection"));
- assertEquals(expectedJson.get("collections"), decrypted.payload.get("collections"));
+ assertTrue(ExtendedJSONObject.equalsJSONObject(
+ (JSONObject) expectedJson.get("collections"),
+ (JSONObject) decrypted.payload.get("collections")
+ ));
// Check that the extracted keys were as expected.
JSONArray keys = new ExtendedJSONObject(decrypted.payload.toJSONString()).getArray("default");
KeyBundle keyBundle = KeyBundle.fromBase64EncodedKeys((String)keys.get(0), (String)keys.get(1));
assertArrayEquals(Base64.decodeBase64(expectedBase64EncryptionKey.getBytes("UTF-8")), keyBundle.getEncryptionKey());
assertArrayEquals(Base64.decodeBase64(expectedBase64HmacKey.getBytes("UTF-8")), keyBundle.getHMACKey());
}
@Test
- public void testTTL() throws UnsupportedEncodingException, CryptoException {
+ public void testTTL() throws UnsupportedEncodingException, CryptoException, JSONException {
Record historyRecord = new HistoryRecord();
CryptoRecord cryptoRecord = historyRecord.getEnvelope();
assertEquals(historyRecord.ttl, cryptoRecord.ttl);
// Very important that ttls are set in outbound envelopes.
- JSONObject o = cryptoRecord.toJSONObject();
+ ExtendedJSONObject o = cryptoRecord.toJSONObject();
assertEquals(cryptoRecord.ttl, o.get("ttl"));
// Most important of all, outbound encrypted record envelopes.
KeyBundle keyBundle = KeyBundle.withRandomKeys();
cryptoRecord.keyBundle = keyBundle;
cryptoRecord.encrypt();
assertEquals(historyRecord.ttl, cryptoRecord.ttl); // Should be preserved.
o = cryptoRecord.toJSONObject();
--- a/mobile/android/services/src/test/java/org/mozilla/android/sync/test/TestRecord.java
+++ b/mobile/android/services/src/test/java/org/mozilla/android/sync/test/TestRecord.java
@@ -1,22 +1,22 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.android.sync.test;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.db.Tab;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.CryptoRecord;
import org.mozilla.gecko.sync.ExtendedJSONObject;
-import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
import org.mozilla.gecko.sync.repositories.domain.HistoryRecord;
import org.mozilla.gecko.sync.repositories.domain.Record;
import org.mozilla.gecko.sync.repositories.domain.RecordParseException;
import org.mozilla.gecko.sync.repositories.domain.TabsRecord;
import java.io.IOException;
@@ -25,19 +25,18 @@ import java.util.ArrayList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(TestRunner.class)
public class TestRecord {
- @SuppressWarnings("static-method")
@Test
- public void testQueryRecord() throws NonObjectJSONException, IOException {
+ public void testQueryRecord() throws JSONException, IOException {
final String expectedGUID = "Bl3n3gpKag3s";
final String testRecord =
"{\"id\":\"" + expectedGUID + "\"," +
" \"type\":\"query\"," +
" \"title\":\"Downloads\"," +
" \"parentName\":\"\"," +
" \"bmkUri\":\"place:transition=7&sort=4\"," +
" \"tags\":[]," +
@@ -69,17 +68,17 @@ public class TestRecord {
public void testRecordGUIDs() {
for (int i = 0; i < 50; ++i) {
CryptoRecord cryptoRecord = new HistoryRecord().getEnvelope();
assertEquals(12, cryptoRecord.guid.length());
}
}
@Test
- public void testRecordEquality() {
+ public void testRecordEquality() throws JSONException {
long now = System.currentTimeMillis();
BookmarkRecord bOne = new BookmarkRecord("abcdefghijkl", "bookmarks", now , false);
BookmarkRecord bTwo = new BookmarkRecord("abcdefghijkl", "bookmarks", now , false);
HistoryRecord hOne = new HistoryRecord("mbcdefghijkm", "history", now , false);
HistoryRecord hTwo = new HistoryRecord("mbcdefghijkm", "history", now , false);
// Identical records.
assertFalse(bOne == bTwo);
@@ -169,21 +168,21 @@ public class TestRecord {
assertTrue(hOne.congruentWith(hTwo));
}
@SuppressWarnings("unchecked")
private void addVisit(HistoryRecord r, JSONObject visit) {
if (r.visits == null) {
r.visits = new JSONArray();
}
- r.visits.add(visit);
+ r.visits.put(visit);
}
@SuppressWarnings("unchecked")
- private JSONObject fakeVisit(long time) {
+ private JSONObject fakeVisit(long time) throws JSONException {
JSONObject object = new JSONObject();
object.put("type", 1L);
object.put("date", time * 1000);
return object;
}
@SuppressWarnings("static-method")
@Test
@@ -213,38 +212,38 @@ public class TestRecord {
@SuppressWarnings({ "unchecked", "static-method" })
@Test
public void testTabsRecordCreation() throws Exception {
final TabsRecord record = new TabsRecord("testGuid");
record.clientName = "test client name";
final JSONArray history1 = new JSONArray();
- history1.add("http://test.com/test1.html");
+ history1.put("http://test.com/test1.html");
final Tab tab1 = new Tab("test title 1", "http://test.com/test1.png", history1, 1000);
final JSONArray history2 = new JSONArray();
- history2.add("http://test.com/test2.html#1");
- history2.add("http://test.com/test2.html#2");
- history2.add("http://test.com/test2.html#3");
+ history2.put("http://test.com/test2.html#1");
+ history2.put("http://test.com/test2.html#2");
+ history2.put("http://test.com/test2.html#3");
final Tab tab2 = new Tab("test title 2", "http://test.com/test2.png", history2, 2000);
record.tabs = new ArrayList<Tab>();
record.tabs.add(tab1);
record.tabs.add(tab2);
final TabsRecord parsed = new TabsRecord();
parsed.initFromEnvelope(CryptoRecord.fromJSONRecord(record.getEnvelope().toJSONString()));
assertEquals(record.guid, parsed.guid);
assertEquals(record.clientName, parsed.clientName);
assertEquals(record.tabs, parsed.tabs);
// Verify that equality test doesn't always return true.
- parsed.tabs.get(0).history.add("http://test.com/different.html");
+ parsed.tabs.get(0).history.put("http://test.com/different.html");
assertFalse(record.tabs.equals(parsed.tabs));
}
public static class URITestBookmarkRecord extends BookmarkRecord {
public static void doTest() {
assertEquals("places:uri=abc%26def+baz&p1=123&p2=bar+baz",
encodeUnsupportedTypeURI("abc&def baz", "p1", "123", "p2", "bar baz"));
assertEquals("places:uri=abc%26def+baz&p1=123",
@@ -274,34 +273,34 @@ public class TestRecord {
"\"mNTdpgoRZMbW\", \"-L8Vci6CbkJY\", \"bVzudKSQERc1\", \"Gxl9lb4DXsmL\"," +
"\"3Qr13GucOtEh\"]}";
public class PayloadBookmarkRecord extends BookmarkRecord {
public PayloadBookmarkRecord() {
super("abcdefghijkl", "bookmarks", 1234, false);
}
- public void doTest() throws NonObjectJSONException, IOException {
+ public void doTest() throws JSONException, IOException {
this.initFromPayload(new ExtendedJSONObject(payload));
assertEquals("abcdefghijkl", this.guid); // Ignores payload.
assertEquals("livemark", this.type);
assertEquals("Bookmarks Toolbar", this.parentName);
assertEquals("toolbar", this.parentID);
assertEquals("", this.description);
assertEquals(null, this.children);
final String encodedSite = "http%3A%2F%2Fwww.bbc.co.uk%2Fgo%2Frss%2Fint%2Fnews%2F-%2Fnews%2F";
final String encodedFeed = "http%3A%2F%2Ffxfeeds.mozilla.com%2Fen-US%2Ffirefox%2Fheadlines.xml";
final String expectedURI = "places:siteUri=" + encodedSite + "&feedUri=" + encodedFeed;
assertEquals(expectedURI, this.bookmarkURI);
}
}
@Test
- public void testUnusualBookmarkRecords() throws NonObjectJSONException, IOException {
+ public void testUnusualBookmarkRecords() throws JSONException, IOException {
PayloadBookmarkRecord record = new PayloadBookmarkRecord();
record.doTest();
}
@SuppressWarnings("static-method")
@Test
public void testTTL() {
Record record = new HistoryRecord();
--- a/mobile/android/services/src/test/java/org/mozilla/android/sync/test/TestResetCommands.java
+++ b/mobile/android/services/src/test/java/org/mozilla/android/sync/test/TestResetCommands.java
@@ -1,14 +1,16 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.android.sync.test;
import android.content.SharedPreferences;
+
+import org.json.JSONException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.DefaultGlobalSessionCallback;
import org.mozilla.gecko.background.testhelpers.MockPrefsGlobalSession;
import org.mozilla.gecko.background.testhelpers.MockServerSyncStage;
import org.mozilla.gecko.background.testhelpers.MockSharedPreferences;
import org.mozilla.gecko.background.testhelpers.TestRunner;
@@ -56,17 +58,17 @@ public class TestResetCommands {
}
@Before
public void setUp() {
assertTrue(WaitHelper.getTestWaiter().isIdle());
}
@Test
- public void testHandleResetCommand() throws SyncConfigurationException, IllegalArgumentException, NonObjectJSONException, IOException, CryptoException {
+ public void testHandleResetCommand() throws SyncConfigurationException, IllegalArgumentException, NonObjectJSONException, JSONException, IOException, CryptoException {
// Create a global session.
// Set up stage mappings for a real stage name (because they're looked up by name
// in an enumeration) pointing to our fake stage.
// Send a reset command.
// Verify that reset is called on our stage.
class Result {
public boolean called = false;
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/background/db/TestClientsDatabase.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/background/db/TestClientsDatabase.java
@@ -1,17 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.background.db;
import android.content.Context;
import android.database.Cursor;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.repositories.NullCursorException;
@@ -73,25 +73,25 @@ public class TestClientsDatabase {
@Test
public void testStoreAndFetchSpecificCommands() {
String accountGUID = Utils.generateGuid();
ArrayList<String> args = new ArrayList<>();
args.add("URI of Page");
args.add("Sender GUID");
args.add("Title of Page");
- String jsonArgs = JSONArray.toJSONString(args);
+ String jsonArgs = new JSONArray(args).toString();
Cursor cur = null;
try {
db.store(accountGUID, "displayURI", jsonArgs, "flowID");
// This row should not show up in the fetch.
args.add("Another arg.");
- db.store(accountGUID, "displayURI", JSONArray.toJSONString(args), "flowID");
+ db.store(accountGUID, "displayURI", new JSONArray(args).toString(), "flowID");
// Test stored item gets fetched correctly.
cur = db.fetchSpecificCommand(accountGUID, "displayURI", jsonArgs);
Assert.assertTrue(cur.moveToFirst());
Assert.assertEquals(1, cur.getCount());
String guid = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_ACCOUNT_GUID);
String commandType = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_COMMAND);
@@ -111,25 +111,25 @@ public class TestClientsDatabase {
@Test
public void testFetchCommandsForClient() {
String accountGUID = Utils.generateGuid();
ArrayList<String> args = new ArrayList<>();
args.add("URI of Page");
args.add("Sender GUID");
args.add("Title of Page");
- String jsonArgs = JSONArray.toJSONString(args);
+ String jsonArgs = new JSONArray(args).toString();
Cursor cur = null;
try {
db.store(accountGUID, "displayURI", jsonArgs, "flowID");
// This row should ALSO show up in the fetch.
args.add("Another arg.");
- db.store(accountGUID, "displayURI", JSONArray.toJSONString(args), "flowID");
+ db.store(accountGUID, "displayURI", new JSONArray(args).toString(), "flowID");
// Test both stored items with the same GUID but different command are fetched.
cur = db.fetchCommandsForClient(accountGUID);
Assert.assertTrue(cur.moveToFirst());
Assert.assertEquals(2, cur.getCount());
} catch (NullCursorException e) {
Assert.fail("Should not have NullCursorException");
} finally {
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/background/db/TestClientsDatabaseAccessor.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/background/db/TestClientsDatabaseAccessor.java
@@ -73,18 +73,18 @@ public class TestClientsDatabaseAccessor
try {
db.store(accountGUID1, command1);
db.store(accountGUID1, command2);
db.store(accountGUID2, command3);
List<Command> commands = db.fetchCommandsForClient(accountGUID1);
Assert.assertEquals(2, commands.size());
- Assert.assertEquals(1, commands.get(0).args.size());
- Assert.assertEquals(1, commands.get(1).args.size());
+ Assert.assertEquals(1, commands.get(0).args.length());
+ Assert.assertEquals(1, commands.get(1).args.length());
} catch (NullCursorException e) {
Assert.fail("Should not have NullCursorException");
}
}
@Test
public void testNumClients() {
final int COUNT = 5;
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/background/db/TestTabsProvider.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/background/db/TestTabsProvider.java
@@ -5,17 +5,17 @@ package org.mozilla.gecko.background.db;
import android.content.ContentProvider;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.sync.repositories.android.BrowserContractHelpers;
@@ -77,28 +77,28 @@ public class TestTabsProvider {
cv.put(BrowserContract.Clients.GUID, TEST_CLIENT_GUID);
cv.put(BrowserContract.Clients.NAME, TEST_CLIENT_NAME);
clientsClient.insert(BrowserContractHelpers.CLIENTS_CONTENT_URI, cv);
}
@SuppressWarnings("unchecked")
protected void insertSomeTestTabs(ContentProviderClient tabsClient) throws RemoteException {
final JSONArray history1 = new JSONArray();
- history1.add("http://test.com/test1.html");
+ history1.put("http://test.com/test1.html");
testTab1 = new Tab("test title 1", "http://test.com/test1.png", history1, 1000);
final JSONArray history2 = new JSONArray();
- history2.add("http://test.com/test2.html#1");
- history2.add("http://test.com/test2.html#2");
- history2.add("http://test.com/test2.html#3");
+ history2.put("http://test.com/test2.html#1");
+ history2.put("http://test.com/test2.html#2");
+ history2.put("http://test.com/test2.html#3");
testTab2 = new Tab("test title 2", "http://test.com/test2.png", history2, 2000);
final JSONArray history3 = new JSONArray();
- history3.add("http://test.com/test3.html#1");
- history3.add("http://test.com/test3.html#2");
+ history3.put("http://test.com/test3.html#1");
+ history3.put("http://test.com/test3.html#2");
testTab3 = new Tab("test title 3", "http://test.com/test3.png", history3, 3000);
tabsClient.insert(BrowserContractHelpers.TABS_CONTENT_URI, testTab1.toContentValues(TEST_CLIENT_GUID, 0));
tabsClient.insert(BrowserContractHelpers.TABS_CONTENT_URI, testTab2.toContentValues(TEST_CLIENT_GUID, 1));
tabsClient.insert(BrowserContractHelpers.TABS_CONTENT_URI, testTab3.toContentValues(TEST_CLIENT_GUID, 2));
}
// Sanity.
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/background/testhelpers/BaseMockServerSyncStage.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/background/testhelpers/BaseMockServerSyncStage.java
@@ -1,15 +1,15 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.background.testhelpers;
+import org.json.JSONException;
import org.mozilla.gecko.sync.NoCollectionKeysSetException;
-import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.SynchronizerConfiguration;
import org.mozilla.gecko.sync.repositories.RecordFactory;
import org.mozilla.gecko.sync.repositories.Repository;
import org.mozilla.gecko.sync.stage.ServerSyncStage;
import java.io.IOException;
import java.net.URISyntaxException;
@@ -61,12 +61,12 @@ public abstract class BaseMockServerSync
@Override
protected Repository wrappedServerRepo()
throws NoCollectionKeysSetException, URISyntaxException {
return getRemoteRepository();
}
public SynchronizerConfiguration leakConfig()
- throws NonObjectJSONException, IOException {
+ throws JSONException, IOException {
return this.getConfig();
}
}
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/background/testhelpers/CommandHelpers.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/background/testhelpers/CommandHelpers.java
@@ -1,40 +1,40 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.background.testhelpers;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.mozilla.gecko.sync.CommandProcessor.Command;
public class CommandHelpers {
@SuppressWarnings("unchecked")
public static Command getCommand1() {
JSONArray args = new JSONArray();
- args.add("argsA");
+ args.put("argsA");
return new Command("displayURI", args, null);
}
@SuppressWarnings("unchecked")
public static Command getCommand2() {
JSONArray args = new JSONArray();
- args.add("argsB");
+ args.put("argsB");
return new Command("displayURI", args, null);
}
@SuppressWarnings("unchecked")
public static Command getCommand3() {
JSONArray args = new JSONArray();
- args.add("argsC");
+ args.put("argsC");
return new Command("displayURI", args, null);
}
@SuppressWarnings("unchecked")
public static Command getCommand4() {
JSONArray args = new JSONArray();
- args.add("URI of Page");
- args.add("Sender ID");
- args.add("Title of Page");
+ args.put("URI of Page");
+ args.put("Sender ID");
+ args.put("Title of Page");
return new Command("displayURI", args, null);
}
}
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/background/testhelpers/MockRecord.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/background/testhelpers/MockRecord.java
@@ -1,14 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.background.testhelpers;
-import org.json.simple.JSONObject;
+import org.json.JSONObject;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.repositories.domain.Record;
import java.util.Random;
public class MockRecord extends Record {
private final int payloadByteCount;
public MockRecord(String guid, String collection, long lastModified, boolean deleted) {
@@ -35,30 +35,30 @@ public class MockRecord extends Record {
public Record copyWithIDs(String guid, long androidID) {
MockRecord r = new MockRecord(guid, this.collection, this.lastModified, this.deleted);
r.androidID = androidID;
return r;
}
@Override
public String toJSONString() {
- return toJSONObject().toJSONString();
+ return toJSONObject().toString();
}
@Override
- public JSONObject toJSONObject() {
+ public ExtendedJSONObject toJSONObject() {
// Build up a randomish payload string based on the length we were asked for.
final Random random = new Random();
final char[] payloadChars = new char[payloadByteCount];
for (int i = 0; i < payloadByteCount; i++) {
payloadChars[i] = (char) (random.nextInt(26) + 'a');
}
final String payloadString = new String(payloadChars);
final ExtendedJSONObject o = new ExtendedJSONObject();
o.put("payload", payloadString);
o.put("id", this.guid);
if (this.ttl > 0) {
o.put("ttl", this.ttl);
}
- return o.object;
+ return o;
}
}
\ No newline at end of file
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/browserid/test/TestRSACryptoImplementation.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/browserid/test/TestRSACryptoImplementation.java
@@ -1,14 +1,16 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.browserid.test;
import junit.framework.Assert;
+
+import org.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.browserid.BrowserIDKeyPair;
import org.mozilla.gecko.browserid.RSACryptoImplementation;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import java.math.BigInteger;
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/sync/middleware/test/TestCrypto5MiddlewareRepositorySession.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/sync/middleware/test/TestCrypto5MiddlewareRepositorySession.java
@@ -1,14 +1,16 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.sync.middleware.test;
import junit.framework.AssertionFailedError;
+
+import org.json.JSONException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.android.sync.test.helpers.ExpectSuccessRepositorySessionFetchRecordsDelegate;
import org.mozilla.android.sync.test.helpers.ExpectSuccessRepositorySessionFinishDelegate;
import org.mozilla.android.sync.test.helpers.ExpectSuccessRepositorySessionStoreDelegate;
import org.mozilla.android.sync.test.helpers.ExpectSuccessRepositoryWipeDelegate;
import org.mozilla.gecko.background.testhelpers.MockRecord;
@@ -22,16 +24,18 @@ import org.mozilla.gecko.sync.crypto.Key
import org.mozilla.gecko.sync.middleware.Crypto5MiddlewareRepository;
import org.mozilla.gecko.sync.middleware.Crypto5MiddlewareRepositorySession;
import org.mozilla.gecko.sync.repositories.InactiveSessionException;
import org.mozilla.gecko.sync.repositories.NoStoreDelegateException;
import org.mozilla.gecko.sync.repositories.RepositorySession;
import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
import org.mozilla.gecko.sync.repositories.domain.Record;
+import java.io.IOException;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@RunWith(TestRunner.class)
public class TestCrypto5MiddlewareRepositorySession {
public static WaitHelper getTestWaiter() {
@@ -66,17 +70,17 @@ public class TestCrypto5MiddlewareReposi
@Before
public void setUp() throws CryptoException {
wboRepo = new WBORepository();
keyBundle = KeyBundle.withRandomKeys();
cmwRepo = new Crypto5MiddlewareRepository(wboRepo, keyBundle);
cmwSession = null;
}
- public void beginSessionAndAssertSuccess() throws Exception{
+ public void beginSessionAndAssertSuccess() throws SyncException {
cmwSession = (Crypto5MiddlewareRepositorySession) cmwRepo.createSession(null);
assertSame(RepositorySession.SessionStatus.UNSTARTED, cmwSession.getStatus());
cmwSession.begin();
assertSame(RepositorySession.SessionStatus.ACTIVE, cmwSession.getStatus());
}
@Test
/**
@@ -126,17 +130,17 @@ public class TestCrypto5MiddlewareReposi
}));
assertEquals(0, wboRepo.wbos.size());
}
@Test
/**
* Verify that store is actually writing encrypted data to the underlying repository.
*/
- public void testStoreEncrypts() throws Exception {
+ public void testStoreEncrypts() throws JSONException, CryptoException, SyncException, IOException {
final BookmarkRecord record = new BookmarkRecord("nncdefghiaaa", "coll", System.currentTimeMillis(), false);
record.title = "unencrypted title";
beginSessionAndAssertSuccess();
performWait(onThreadRunnable(new Runnable() {
@Override
public void run() {
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/sync/repositories/android/VisitsHelperTest.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/sync/repositories/android/VisitsHelperTest.java
@@ -5,18 +5,18 @@ package org.mozilla.gecko.sync.repositor
import android.content.ContentProvider;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.net.Uri;
import junit.framework.Assert;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.db.DelegatingTestContentProvider;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.db.BrowserContract;
import org.robolectric.shadows.ShadowContentResolver;
import static org.junit.Assert.assertEquals;
@@ -25,25 +25,25 @@ import static org.junit.Assert.assertTru
@RunWith(TestRunner.class)
public class VisitsHelperTest {
@Test
public void testBulkInsertRemoteVisits() throws Exception {
JSONArray toInsert = new JSONArray();
Assert.assertEquals(0, VisitsHelper.getVisitsContentValues("testGUID", toInsert).length);
JSONObject visit = new JSONObject();
- Long date = Long.valueOf(123432552344l);
+ Long date = 123432552344L;
visit.put("date", date);
- visit.put("type", 2l);
- toInsert.add(visit);
+ visit.put("type", 2L);
+ toInsert.put(visit);
JSONObject visit2 = new JSONObject();
visit2.put("date", date + 1000);
- visit2.put("type", 5l);
- toInsert.add(visit2);
+ visit2.put("type", 5L);
+ toInsert.put(visit2);
ContentValues[] cvs = VisitsHelper.getVisitsContentValues("testGUID", toInsert);
Assert.assertEquals(2, cvs.length);
ContentValues cv1 = cvs[0];
ContentValues cv2 = cvs[1];
Assert.assertEquals(Integer.valueOf(2), cv1.getAsInteger(BrowserContract.Visits.VISIT_TYPE));
Assert.assertEquals(Integer.valueOf(5), cv2.getAsInteger(BrowserContract.Visits.VISIT_TYPE));
@@ -74,33 +74,33 @@ public class VisitsHelperTest {
visitItem.put(BrowserContract.Visits.DATE_VISITED, baseDate - i * 100);
visitItem.put(BrowserContract.Visits.VISIT_TYPE, 1);
visitItem.put(BrowserContract.Visits.IS_LOCAL, 1);
visitsClient.insert(visitsTestUri, visitItem);
}
// test that limit worked, that sorting is correct, and that both date and type are present
JSONArray recentVisits = VisitsHelper.getRecentHistoryVisitsForGUID(visitsClient, "testGUID", 10);
- Assert.assertEquals(10, recentVisits.size());
- for (int i = 0; i < recentVisits.size(); i++) {
+ Assert.assertEquals(10, recentVisits.length());
+ for (int i = 0; i < recentVisits.length(); i++) {
JSONObject v = (JSONObject) recentVisits.get(i);
- Long date = (Long) v.get("date");
- Long type = (Long) v.get("type");
+ Long date = v.getLong("date");
+ Long type = v.getLong("type");
Assert.assertEquals(Long.valueOf(baseDate - i * 100), date);
Assert.assertEquals(Long.valueOf(1), type);
}
} finally {
provider.shutdown();
}
}
@Test
public void testGetVisitContentValues() throws Exception {
JSONObject visit = new JSONObject();
- Long date = Long.valueOf(123432552344l);
+ Long date = 123432552344L;
visit.put("date", date);
visit.put("type", Long.valueOf(2));
ContentValues cv = VisitsHelper.getVisitContentValues("testGUID", visit, true);
assertTrue(cv.containsKey(BrowserContract.Visits.VISIT_TYPE));
assertTrue(cv.containsKey(BrowserContract.Visits.DATE_VISITED));
assertTrue(cv.containsKey(BrowserContract.Visits.HISTORY_GUID));
assertTrue(cv.containsKey(BrowserContract.Visits.IS_LOCAL));
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/sync/repositories/domain/test/TestPasswordRecord.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/sync/repositories/domain/test/TestPasswordRecord.java
@@ -1,13 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.sync.repositories.domain.test;
+import org.json.JSONException;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.repositories.domain.PasswordRecord;
@@ -27,20 +28,18 @@ public class TestPasswordRecord {
"\"httpRealm\": null,\n" +
"\"username\": \"johndoe\",\n" +
"\"password\": \"p4ssw0rd\",\n" +
"\"usernameField\": \"user\",\n" +
"\"passwordField\": \"pass\",\n" +
// Above the max sane value to ensure we don't regress 1404044
"\"timeLastUsed\": 18446732429235952000" +
"}");
- } catch (IOException e) {
+ } catch (JSONException e) {
Assert.fail("Somehow got an IOException when parsing json from a string D:");
- } catch (NonObjectJSONException e) {
- Assert.fail(e.getMessage());
}
PasswordRecord p = new PasswordRecord();
p.initFromPayload(o);
assertEquals(p.encryptedUsername, o.getString("username"));
assertEquals(p.encryptedPassword, o.getString("password"));
assertEquals(p.usernameField, o.getString("usernameField"));
assertEquals(p.passwordField, o.getString("passwordField"));
assertEquals(p.formSubmitURL, o.getString("formSubmitURL"));
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/sync/repositories/uploaders/PayloadUploadDelegateTest.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/sync/repositories/uploaders/PayloadUploadDelegateTest.java
@@ -1,15 +1,16 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.sync.repositories.uploaders;
import android.net.Uri;
+import org.json.JSONException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.CollectionConcurrentModificationException;
import org.mozilla.gecko.sync.HTTPFailureException;
import org.mozilla.gecko.sync.InfoConfiguration;
@@ -197,19 +198,19 @@ public class PayloadUploadDelegateTest {
postedGuids.add("testGuid2");
PayloadUploadDelegate payloadUploadDelegate = new PayloadUploadDelegate(
authHeaderProvider, payloadDispatcher, postedGuids, false, true);
// Test that we catch json processing errors
payloadUploadDelegate.handleRequestSuccess(makeSyncStorageResponse(200, "non json body", "123"));
assertEquals(2, ((MockPayloadDispatcher) payloadDispatcher).failedRecords.size());
assertTrue(((MockPayloadDispatcher) payloadDispatcher).didPayloadFail);
- assertEquals(NonObjectJSONException.class,
+ assertEquals(JSONException.class,
((MockPayloadDispatcher) payloadDispatcher).failedRecords.get("testGuid1").getClass());
- assertEquals(NonObjectJSONException.class,
+ assertEquals(JSONException.class,
((MockPayloadDispatcher) payloadDispatcher).failedRecords.get("testGuid2").getClass());
}
@Test
public void testHandleRequestSuccess202NoToken() {
ArrayList<String> postedGuids = new ArrayList<>(1);
postedGuids.add("testGuid1");
PayloadUploadDelegate payloadUploadDelegate = new PayloadUploadDelegate(
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/sync/stage/test/TestFetchMetaGlobalStage.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/sync/stage/test/TestFetchMetaGlobalStage.java
@@ -1,62 +1,57 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.sync.stage.test;
import android.os.SystemClock;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
+import org.json.JSONException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.android.sync.net.test.TestGlobalSession;
import org.mozilla.android.sync.net.test.TestMetaGlobal;
import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
import org.mozilla.android.sync.test.helpers.MockGlobalSessionCallback;
import org.mozilla.android.sync.test.helpers.MockServer;
import org.mozilla.gecko.background.testhelpers.MockGlobalSession;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.background.testhelpers.WaitHelper;
import org.mozilla.gecko.sync.AlreadySyncingException;
import org.mozilla.gecko.sync.CollectionKeys;
import org.mozilla.gecko.sync.CryptoRecord;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.GlobalSession;
-import org.mozilla.gecko.sync.HTTPFailureException;
import org.mozilla.gecko.sync.InfoCollections;
import org.mozilla.gecko.sync.MetaGlobal;
import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.SyncConfigurationException;
+import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.crypto.CryptoException;
import org.mozilla.gecko.sync.crypto.KeyBundle;
import org.mozilla.gecko.sync.delegates.FreshStartDelegate;
import org.mozilla.gecko.sync.delegates.GlobalSessionCallback;
import org.mozilla.gecko.sync.delegates.KeyUploadDelegate;
import org.mozilla.gecko.sync.delegates.WipeServerDelegate;
import org.mozilla.gecko.sync.net.AuthHeaderProvider;
-import org.mozilla.gecko.sync.net.SyncStorageResponse;
import org.mozilla.gecko.sync.stage.FetchMetaGlobalStage;
import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
import org.simpleframework.http.Request;
import org.simpleframework.http.Response;
import java.io.IOException;
import java.net.URI;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
-import ch.boye.httpclientandroidlib.HttpResponse;
-import ch.boye.httpclientandroidlib.ProtocolVersion;
-import ch.boye.httpclientandroidlib.message.BasicHttpResponse;
-import ch.boye.httpclientandroidlib.message.BasicStatusLine;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@RunWith(TestRunner.class)
public class TestFetchMetaGlobalStage {
@SuppressWarnings("unused")
private static final String LOG_TAG = "TestMetaGlobalStage";
@@ -75,19 +70,20 @@ public class TestFetchMetaGlobalStage {
private static final String TEST_SYNC_ID = "testSyncID";
private static final long TEST_STORAGE_VERSION = GlobalSession.STORAGE_VERSION;
private InfoCollections infoCollections;
private KeyBundle syncKeyBundle;
private MockGlobalSessionCallback callback;
private LocalMockGlobalSession session;
- private static void assertSameContents(JSONArray expected, Set<String> actual) {
- assertEquals(expected.size(), actual.size());
- for (Object o : expected) {
+ private static void assertSameContents(JSONArray expected, Set<String> actual) throws JSONException {
+ assertEquals(expected.length(), actual.size());
+ for (int i = 0; i < expected.length(); i++) {
+ final Object o = expected.get(i);
assertTrue(actual.contains(o));
}
}
private class LocalMockGlobalSession extends MockGlobalSession {
private boolean calledRequiresUpgrade = false;
private boolean calledProcessMissingMetaGlobal = false;
private boolean calledFreshStart = false;
@@ -207,17 +203,17 @@ public class TestFetchMetaGlobalStage {
assertEquals(true, callback.calledError);
assertTrue(session.calledRequiresUpgrade);
}
@SuppressWarnings("unchecked")
private JSONArray makeTestDeclinedArray() {
final JSONArray declined = new JSONArray();
- declined.add("foobar");
+ declined.put("foobar");
return declined;
}
/**
* Verify that a fetched meta/global with remote syncID == local syncID does
* not reset.
*
* @throws Exception
@@ -227,17 +223,17 @@ public class TestFetchMetaGlobalStage {
session.config.syncID = TEST_SYNC_ID;
MetaGlobal mg = new MetaGlobal(null, null);
mg.setSyncID(TEST_SYNC_ID);
mg.setStorageVersion(Long.valueOf(TEST_STORAGE_VERSION));
// Set declined engines in the server object.
final JSONArray testingDeclinedEngines = makeTestDeclinedArray();
- mg.setDeclinedEngineNames(testingDeclinedEngines);
+ mg.setDeclinedEngineNames(Utils.jsonArrayToHashSet(testingDeclinedEngines));
MockServer server = new MockServer(200, mg.asCryptoRecord().toJSONString());
doSession(server);
assertTrue(callback.calledSuccess);
assertFalse(session.calledProcessMissingMetaGlobal);
assertFalse(session.calledResetAllStages);
assertEquals(TEST_SYNC_ID, session.config.metaGlobal.getSyncID());
@@ -260,17 +256,17 @@ public class TestFetchMetaGlobalStage {
session.config.syncID = "NOT TEST SYNC ID";
MetaGlobal mg = new MetaGlobal(null, null);
mg.setSyncID(TEST_SYNC_ID);
mg.setStorageVersion(Long.valueOf(TEST_STORAGE_VERSION));
// Set declined engines in the server object.
final JSONArray testingDeclinedEngines = makeTestDeclinedArray();
- mg.setDeclinedEngineNames(testingDeclinedEngines);
+ mg.setDeclinedEngineNames(Utils.jsonArrayToHashSet(testingDeclinedEngines));
MockServer server = new MockServer(200, mg.asCryptoRecord().toJSONString());
doSession(server);
assertEquals(true, callback.calledSuccess);
assertFalse(session.calledProcessMissingMetaGlobal);
assertTrue(session.calledResetAllStages);
assertEquals(TEST_SYNC_ID, session.config.metaGlobal.getSyncID());
@@ -287,36 +283,34 @@ public class TestFetchMetaGlobalStage {
* TODO: eventually it should!
*/
@SuppressWarnings("unchecked")
@Test
public void testFetchSuccessWithDifferentSyncIDMergesDeclined() throws Exception {
session.config.syncID = "NOT TEST SYNC ID";
// Fake the local declined engine names.
- session.config.declinedEngineNames = new HashSet<String>();
+ session.config.declinedEngineNames = new HashSet<>();
session.config.declinedEngineNames.add("baznoo");
MetaGlobal mg = new MetaGlobal(null, null);
mg.setSyncID(TEST_SYNC_ID);
mg.setStorageVersion(Long.valueOf(TEST_STORAGE_VERSION));
// Set declined engines in the server object.
final JSONArray testingDeclinedEngines = makeTestDeclinedArray();
- mg.setDeclinedEngineNames(testingDeclinedEngines);
+ final HashSet<String> testingDeclinedHashSet = Utils.jsonArrayToHashSet(testingDeclinedEngines);
+ mg.setDeclinedEngineNames(testingDeclinedHashSet);
MockServer server = new MockServer(200, mg.asCryptoRecord().toJSONString());
doSession(server);
// Declined engines propagate from the server meta/global, and are NOT merged.
- final Set<String> expected = new HashSet<String>(testingDeclinedEngines);
- // expected.add("baznoo"); // Not until we merge. Local is lost.
-
final Set<String> newDeclined = session.config.metaGlobal.getDeclinedEngineNames();
- assertEquals(expected, newDeclined);
+ assertEquals(testingDeclinedHashSet, newDeclined);
}
@Test
public void testFetchMissing() throws Exception {
MockServer server = new MockServer(404, "missing");
doSession(server);
assertEquals(true, callback.calledError);
@@ -353,17 +347,17 @@ public class TestFetchMetaGlobalStage {
* @throws Exception
*/
@Test
public void testFetchMalformedPayload() throws Exception {
MockServer server = new MockServer(200, TestMetaGlobal.TEST_META_GLOBAL_MALFORMED_PAYLOAD_RESPONSE);
doSession(server);
assertEquals(true, callback.calledError);
- assertEquals(NonObjectJSONException.class, callback.calledErrorException.getClass());
+ assertEquals(JSONException.class, callback.calledErrorException.getClass());
}
protected void doFreshStart(MockServer server) {
data.startHTTPServer(server);
WaitHelper.getTestWaiter().performWait(WaitHelper.onThreadRunnable(new Runnable() {
@Override
public void run() {
session.freshStart();
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/sync/telemetry/TelemetryCollectorTest.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/sync/telemetry/TelemetryCollectorTest.java
@@ -1,16 +1,15 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.sync.telemetry;
import android.os.Bundle;
-import org.json.simple.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.CollectionConcurrentModificationException;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.HTTPFailureException;
import org.mozilla.gecko.sync.SyncDeadlineReachedException;
@@ -141,25 +140,25 @@ public class TelemetryCollectorTest {
Bundle data = collector.build();
assertFalse(data.containsKey("error"));
// Test various ways to set an error.
// Just exception.
collector.setError("exceptionTest", new IllegalArgumentException());
data = collector.build();
assertTrue(data.containsKey("error"));
- JSONObject errorJson = (JSONObject) data.getSerializable("error");
+ ExtendedJSONObject errorJson = new ExtendedJSONObject(data.getString("error"));
assertEquals("exceptionTest", errorJson.get("name"));
assertEquals("IllegalArgumentException", errorJson.get("error"));
// Details and exception.
collector.setError("anotherTest", new ConcurrentModificationException(), "Error details");
data = collector.build();
assertTrue(data.containsKey("error"));
- errorJson = (JSONObject) data.getSerializable("error");
+ errorJson = new ExtendedJSONObject(data.getString("error"));
assertEquals("anotherTest", errorJson.get("name"));
assertEquals("ConcurrentModificationException:Error details", errorJson.get("error"));
}
@Test
public void testRestarted() throws Exception {
collector.setStarted(5L);
collector.setFinished(10L);
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/sync/test/TestBookmarkValidator.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/sync/test/TestBookmarkValidator.java
@@ -1,13 +1,13 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.sync.test;
-import org.json.simple.JSONArray;
+import org.json.JSONArray;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
import org.mozilla.gecko.sync.validation.BookmarkValidationResults;
import org.mozilla.gecko.sync.validation.BookmarkValidator;
import static org.junit.Assert.*;
@@ -153,13 +153,15 @@ public class TestBookmarkValidator {
}
private BookmarkRecord createTombstone(String guid) {
return new BookmarkRecord(guid, BookmarkRecord.COLLECTION_NAME, 0, true);
}
@SuppressWarnings("unchecked")
private JSONArray childrenFromGUIDs(String... guids) {
- JSONArray children = new JSONArray();
- children.addAll(Arrays.asList(guids));
+ final JSONArray children = new JSONArray();
+ for (String guid : guids) {
+ children.put(guid);
+ }
return children;
}
}
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/sync/test/TestExtendedJSONObject.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/sync/test/TestExtendedJSONObject.java
@@ -1,21 +1,20 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.sync.test;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.ExtendedJSONObject;
-import org.mozilla.gecko.sync.NonArrayJSONException;
-import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.UnexpectedJSONException.BadRequiredFieldJSONException;
import java.io.IOException;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -25,40 +24,40 @@ import static org.junit.Assert.assertNot
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(TestRunner.class)
public class TestExtendedJSONObject {
public static String exampleJSON = "{\"modified\":1233702554.25,\"success\":[\"{GXS58IDC}12\",\"{GXS58IDC}13\",\"{GXS58IDC}15\",\"{GXS58IDC}16\",\"{GXS58IDC}18\",\"{GXS58IDC}19\"],\"failed\":{\"{GXS58IDC}11\":[\"invalid parentid\"],\"{GXS58IDC}14\":[\"invalid parentid\"],\"{GXS58IDC}17\":[\"invalid parentid\"],\"{GXS58IDC}20\":[\"invalid parentid\"]}}";
- public static String exampleIntegral = "{\"modified\":1233702554,}";
+ public static String exampleIntegral = "{\"modified\":1233702554}";
@Test
- public void testFractional() throws IOException, NonObjectJSONException {
+ public void testFractional() throws IOException, JSONException {
ExtendedJSONObject o = new ExtendedJSONObject(exampleJSON);
assertTrue(o.containsKey("modified"));
assertTrue(o.containsKey("success"));
assertTrue(o.containsKey("failed"));
assertFalse(o.containsKey(" "));
assertFalse(o.containsKey(""));
assertFalse(o.containsKey("foo"));
assertTrue(o.get("modified") instanceof Number);
assertTrue(o.get("modified").equals(Double.parseDouble("1233702554.25")));
assertEquals(Long.valueOf(1233702554250L), o.getTimestamp("modified"));
assertEquals(null, o.getTimestamp("foo"));
}
@Test
- public void testIntegral() throws IOException, NonObjectJSONException {
+ public void testIntegral() throws IOException, JSONException {
ExtendedJSONObject o = new ExtendedJSONObject(exampleIntegral);
assertTrue(o.containsKey("modified"));
assertFalse(o.containsKey("success"));
assertTrue(o.get("modified") instanceof Number);
- assertTrue(o.get("modified").equals(Long.parseLong("1233702554")));
+ assertTrue(o.get("modified").equals(1233702554));
assertEquals(Long.valueOf(1233702554000L), o.getTimestamp("modified"));
assertEquals(null, o.getTimestamp("foo"));
}
@Test
public void testSafeInteger() {
ExtendedJSONObject o = new ExtendedJSONObject();
o.put("integer", Integer.valueOf(5));
@@ -71,34 +70,34 @@ public class TestExtendedJSONObject {
assertNull(o.getIntegerSafely(null));
}
@Test
public void testParseJSONArray() throws Exception {
JSONArray result = ExtendedJSONObject.parseJSONArray("[0, 1, {\"test\": 2}]");
assertNotNull(result);
- assertThat((Long) result.get(0), is(equalTo(0L)));
- assertThat((Long) result.get(1), is(equalTo(1L)));
- assertThat((Long) ((JSONObject) result.get(2)).get("test"), is(equalTo(2L)));
+ assertThat((Integer) result.get(0), is(equalTo(0)));
+ assertThat((Integer) result.get(1), is(equalTo(1)));
+ assertThat((Integer) ((JSONObject) result.get(2)).get("test"), is(equalTo(2)));
}
@Test
public void testBadParseJSONArray() throws Exception {
try {
ExtendedJSONObject.parseJSONArray("[0, ");
fail();
- } catch (NonArrayJSONException e) {
+ } catch (JSONException e) {
// Do nothing.
}
try {
ExtendedJSONObject.parseJSONArray("{}");
fail();
- } catch (NonArrayJSONException e) {
+ } catch (JSONException e) {
// Do nothing.
}
}
@Test
public void testParseUTF8AsJSONObject() throws Exception {
String TEST = "{\"key\":\"value\"}";
@@ -107,24 +106,24 @@ public class TestExtendedJSONObject {
assertEquals("value", o.getString("key"));
}
@Test
public void testBadParseUTF8AsJSONObject() throws Exception {
try {
ExtendedJSONObject.parseUTF8AsJSONObject("{}".getBytes("UTF-16"));
fail();
- } catch (NonObjectJSONException e) {
+ } catch (JSONException e) {
// Do nothing.
}
try {
ExtendedJSONObject.parseUTF8AsJSONObject("{".getBytes("UTF-8"));
fail();
- } catch (NonObjectJSONException e) {
+ } catch (JSONException e) {
// Do nothing.
}
}
@Test
public void testHashCode() throws Exception {
ExtendedJSONObject o = new ExtendedJSONObject(exampleJSON);
assertEquals(o.hashCode(), o.hashCode());
@@ -167,37 +166,37 @@ public class TestExtendedJSONObject {
ExtendedJSONObject o = new ExtendedJSONObject("{\"x\": null}");
Long x = o.getLong("x");
assertNull(x);
long y = o.getLong("x", 5L);
assertEquals(5L, y);
}
- protected void assertException(ExtendedJSONObject o, String[] requiredFields, Class<?> requiredFieldClass) {
+ private void assertException(ExtendedJSONObject o, String[] requiredFields, Class<?> requiredFieldClass) {
try {
o.throwIfFieldsMissingOrMisTyped(requiredFields, requiredFieldClass);
fail();
} catch (Exception e) {
assertTrue(e instanceof BadRequiredFieldJSONException);
}
}
@Test
public void testThrow() throws Exception {
ExtendedJSONObject o = new ExtendedJSONObject("{\"true\":true, \"false\":false, \"string\":\"string\", \"long\":40000000000, \"int\":40, \"nested\":{\"inner\":10}}");
o.throwIfFieldsMissingOrMisTyped(new String[] { "true", "false" }, Boolean.class);
o.throwIfFieldsMissingOrMisTyped(new String[] { "string" }, String.class);
o.throwIfFieldsMissingOrMisTyped(new String[] { "long" }, Long.class);
- o.throwIfFieldsMissingOrMisTyped(new String[] { "int" }, Long.class);
+ o.throwIfFieldsMissingOrMisTyped(new String[] { "int" }, Integer.class);
o.throwIfFieldsMissingOrMisTyped(new String[] { "int" }, null);
// Perhaps a bit unexpected, but we'll document it here.
o.throwIfFieldsMissingOrMisTyped(new String[] { "nested" }, JSONObject.class);
// Should fail.
- assertException(o, new String[] { "int" }, Integer.class); // Irritating, but...
+ assertException(o, new String[] { "int" }, Long.class); // Ditto.
assertException(o, new String[] { "long" }, Integer.class); // Ditto.
assertException(o, new String[] { "missing" }, String.class);
assertException(o, new String[] { "missing" }, null);
- assertException(o, new String[] { "string", "int" }, String.class); // Irritating, but...
+ assertException(o, new String[] { "int" }, String.class);
}
}
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/sync/test/TestPersistedMetaGlobal.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/sync/test/TestPersistedMetaGlobal.java
@@ -81,17 +81,17 @@ public class TestPersistedMetaGlobal {
public void testPersistDeclinedEngines() throws Exception {
PersistedMetaGlobal persisted = new PersistedMetaGlobal(prefs);
AuthHeaderProvider authHeaderProvider = new BasicAuthHeaderProvider(TEST_CREDENTIALS);
// Test fresh start.
assertNull(persisted.metaGlobal(TEST_META_URL, authHeaderProvider));
// Test persisting.
- String body = "{\"id\":\"global\",\"payload\":\"{\\\"declined\\\":[\\\"bookmarks\\\",\\\"addons\\\"],\\\"syncID\\\":\\\"zPSQTm7WBVWB\\\",\\\"storageVersion\\\":5,\\\"engines\\\":{\\\"clients\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"fDg0MS5bDtV7\\\"},,\\\"forms\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"GXF29AFprnvc\\\"},\\\"history\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"av75g4vm-_rp\\\"},\\\"passwords\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"LT_ACGpuKZ6a\\\"},\\\"prefs\\\":{\\\"version\\\":2,\\\"syncID\\\":\\\"-3nsksP9wSAs\\\"},\\\"tabs\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"W4H5lOMChkYA\\\"}}}\",\"username\":\"5817483\",\"modified\":1.32046073744E9}";
+ String body = "{\"id\":\"global\",\"payload\":\"{\\\"declined\\\":[\\\"bookmarks\\\",\\\"addons\\\"],\\\"syncID\\\":\\\"zPSQTm7WBVWB\\\",\\\"storageVersion\\\":5,\\\"engines\\\":{\\\"clients\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"fDg0MS5bDtV7\\\"},\\\"forms\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"GXF29AFprnvc\\\"},\\\"history\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"av75g4vm-_rp\\\"},\\\"passwords\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"LT_ACGpuKZ6a\\\"},\\\"prefs\\\":{\\\"version\\\":2,\\\"syncID\\\":\\\"-3nsksP9wSAs\\\"},\\\"tabs\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"W4H5lOMChkYA\\\"}}}\",\"username\":\"5817483\",\"modified\":1.32046073744E9}";
MetaGlobal mg = new MetaGlobal(TEST_META_URL, authHeaderProvider);
mg.setFromRecord(CryptoRecord.fromJSONRecord(body));
persisted.persistMetaGlobal(mg);
MetaGlobal persistedGlobal = persisted.metaGlobal(TEST_META_URL, authHeaderProvider);
assertNotNull(persistedGlobal);
Set<String> declined = persistedGlobal.getDeclinedEngineNames();
assertEquals(2, declined.size());