java/syncbase: Add Cloud support in Android HLAPI

Init now uses an Options Builder that allow you to go the cloud-
supported route or the offline route.

This also carries through the logLevel changes from v.io/c/23754

Also
- removes sUserdataCollection in favor of accessing it via
sDatabase.
- Calls the loginCallback.onSuccess() via executor.
- Adds the proxy flag (-v23.proxy=proxy) to allow devices to
  communicate across networks and with the cloud.

MultiPart: 2/2
Change-Id: I26f3ac17fcfe0597b92b8c9cb427e17bc15cf272
diff --git a/projects/dice_roller/app/build.gradle b/projects/dice_roller/app/build.gradle
index a434224..d73c49a 100644
--- a/projects/dice_roller/app/build.gradle
+++ b/projects/dice_roller/app/build.gradle
@@ -32,5 +32,5 @@
     compile fileTree(dir: 'libs', include: ['*.jar'])
     testCompile 'junit:junit:4.12'
     compile 'com.android.support:appcompat-v7:23.4.0'
-    compile 'io.v:syncbase:0.1.6'
+    compile 'io.v:syncbase:0.1.7'
 }
diff --git a/projects/dice_roller/app/src/main/java/v/io/diceroller/MainActivity.java b/projects/dice_roller/app/src/main/java/v/io/diceroller/MainActivity.java
index 722120e..9d3b61c 100644
--- a/projects/dice_roller/app/src/main/java/v/io/diceroller/MainActivity.java
+++ b/projects/dice_roller/app/src/main/java/v/io/diceroller/MainActivity.java
@@ -27,24 +27,21 @@
     private static final String TAG = "DiceRoller";
     private static final String RESULT_KEY = "result";
 
+    private static final String CLOUD_NAME =
+            "/(dev.v.io:r:vprod:service:mounttabled)@ns.dev.v.io:8101/sb/syncbased-24204641";
+    private static final String CLOUD_ADMIN = "dev.v.io:r:allocator:us:x:syncbased-24204641";
+    private static final String MOUNT_POINT = "/ns.dev.v.io:8101/tmp/diceroller/users";
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
 
         try {
-            Syncbase.Options options = new Syncbase.Options();
-            List<String> mountpoints = new ArrayList<>();
-            mountpoints.add("/ns.dev.v.io:8101/tmp/diceroller/users");
-            options.mountPoints = mountpoints;
-
-            // TODO(alexfandrianto): Write an Android helper in HLAPI to determine this location.
-            // https://github.com/vanadium/issues/issues/1384
-            options.rootDir = getDir("syncbase", Context.MODE_PRIVATE).getAbsolutePath();
-
-            // TODO(alexfandrianto): Disabled until we set up a cloud.
-            // https://github.com/vanadium/issues/issues/1357
-            options.disableSyncgroupPublishing = true;
+            String rootDir = getDir("syncbase", Context.MODE_PRIVATE).getAbsolutePath();
+            Syncbase.Options options =
+                    Syncbase.Options.cloudBuilder(rootDir, CLOUD_NAME, CLOUD_ADMIN)
+                            .setMountPoint(MOUNT_POINT).build();
             Syncbase.init(options);
         } catch (SyncbaseException e) {
             Log.e(TAG, e.toString());
diff --git a/syncbase/src/main/java/io/v/syncbase/Database.java b/syncbase/src/main/java/io/v/syncbase/Database.java
index 724d866..6232319 100644
--- a/syncbase/src/main/java/io/v/syncbase/Database.java
+++ b/syncbase/src/main/java/io/v/syncbase/Database.java
@@ -188,7 +188,7 @@
                     @Override
                     public void onInvite(final io.v.syncbase.core.SyncgroupInvite invite) {
                         final SettableFuture<Boolean> setFuture = SettableFuture.create();
-                        Syncbase.sOpts.callbackExecutor.execute(new Runnable() {
+                        Syncbase.sOpts.mCallbackExecutor.execute(new Runnable() {
                             @Override
                             public void run() {
                                 h.onInvite(new SyncgroupInvite(new Id(invite.syncgroup),
@@ -267,10 +267,10 @@
             @Override
             public void run() {
                 try {
-                    String publishName = Syncbase.sOpts.getPublishSyncbaseName(); // ok if null
+                    String publishName = Syncbase.sOpts.mCloudName; // ok if null
                     List<String> expectedBlessings = invite.getInviterBlessingNames();
-                    if (Syncbase.sOpts.getCloudBlessingString() != null) {
-                        expectedBlessings.add(Syncbase.sOpts.getCloudBlessingString());
+                    if (Syncbase.sOpts.mCloudAdmin != null) {
+                        expectedBlessings.add(Syncbase.sOpts.mCloudAdmin);
                     }
                     coreSyncgroup.join(publishName, expectedBlessings, new SyncgroupMemberInfo());
                     Syncbase.addToUserdata(invite.getId());
@@ -524,7 +524,7 @@
                             if (!mGotFirstBatch) {
                                 mGotFirstBatch = true;
                                 final List<WatchChange> cpBatch = mBatch;
-                                Syncbase.sOpts.callbackExecutor.execute(new Runnable() {
+                                Syncbase.sOpts.mCallbackExecutor.execute(new Runnable() {
                                     @Override
                                     public void run() {
                                         h.onInitialState(cpBatch.iterator());
@@ -533,7 +533,7 @@
                                 });
                             } else {
                                 final List<WatchChange> cpBatch = mBatch;
-                                Syncbase.sOpts.callbackExecutor.execute(new Runnable() {
+                                Syncbase.sOpts.mCallbackExecutor.execute(new Runnable() {
                                     @Override
                                     public void run() {
                                         h.onChangeBatch(cpBatch.iterator());
diff --git a/syncbase/src/main/java/io/v/syncbase/Syncbase.java b/syncbase/src/main/java/io/v/syncbase/Syncbase.java
index a4c3308..c955244 100644
--- a/syncbase/src/main/java/io/v/syncbase/Syncbase.java
+++ b/syncbase/src/main/java/io/v/syncbase/Syncbase.java
@@ -57,33 +57,136 @@
      * Options for opening a database.
      */
     public static class Options {
-        // The executor used to execute callbacks.
-        public Executor callbackExecutor = UiThreadExecutor.INSTANCE;
-        // Where data should be persisted.
-        public String rootDir;
-        // We use an empty mountPoints to avoid talking to the global mounttabled.
-        public List<String> mountPoints = new ArrayList<>();
-        // TODO(sadovsky): Figure out how developers should specify this.
-        public String adminUserId = "alexfandrianto@google.com";
-        // TODO(sadovsky): Figure out how developers should specify this.
-        public String defaultBlessingStringPrefix = "dev.v.io:o:608941808256-43vtfndets79kf5hac8ieujto8837660.apps.googleusercontent.com:";
-        // FOR ADVANCED USERS. If true, syncgroups will not be published to the cloud peer.
-        public boolean disableSyncgroupPublishing;
-        // FOR ADVANCED USERS. If true, the user's data will not be synced across their devices.
-        public boolean disableUserdataSyncgroup;
-        // FOR TESTING. If true, the app name is set to 'app', the user name is set to 'user' and
-        // the arguments to login() are ignored.
-        public boolean testLogin;
+        final Executor mCallbackExecutor;
+        final String mRootDir;
+        final List<String> mMountPoints;
+        final boolean mDisableSyncgroupPublishing;
+        final boolean mDisableUserdataSyncgroup;
+        final boolean mTestLogin;
+        final int mLogLevel;
 
-        String getPublishSyncbaseName() {
-            if (disableSyncgroupPublishing) {
-                return null;
-            }
-            return mountPoints.get(0) + "cloud";
+        final String mCloudName;
+        final String mCloudAdmin;
+
+        Options(Options.Builder builder) {
+            mCallbackExecutor = builder.mExecutor;
+            mRootDir = builder.mRootDir;
+            mMountPoints = builder.mMountPoints;
+            mDisableSyncgroupPublishing = !builder.mUsesCloud;
+            mDisableUserdataSyncgroup = !builder.mUsesCloud;
+            mTestLogin = builder.mTestLogin;
+            mLogLevel = builder.mLogLevel;
+
+            mCloudName = builder.mCloudName;
+            mCloudAdmin = builder.mCloudAdmin;
         }
 
-        String getCloudBlessingString() {
-            return "dev.v.io:u:" + adminUserId;
+        /**
+         * Builds options used to create an app that needs a cloud for initial bootstrapping and
+         * increased data availability. Apps that use a cloud will automatically synchronize data
+         * across all of the same user's devices. To allocate a cloud instance of Syncbase, visit
+         * https://sb-allocator.v.io/home
+         *
+         * @param rootDir Directory to store data.
+         * @param cloudName Name of the cloud. See https://sb-allocator.v.io/home
+         * @param cloudAdmin The cloud's blessing patterns. See https://sb-allocator.v.io/home
+         */
+        public static Options.Builder cloudBuilder(String rootDir, String cloudName, String cloudAdmin) {
+            return new Options.Builder(rootDir, cloudName, cloudAdmin);
+        }
+        /**
+         * Builds options used to create an app that primarily runs offline.
+         *
+         * @param rootDir Directory to store data.
+         */
+        public static Options.Builder offlineBuilder(String rootDir) {
+            return new Options.Builder(rootDir);
+        }
+
+        public static class Builder {
+            private final boolean mUsesCloud;
+            private final String mRootDir;
+            private final String mCloudName;
+            private final String mCloudAdmin;
+
+            private Executor mExecutor = UiThreadExecutor.INSTANCE;
+            private final List<String> mMountPoints = new ArrayList<>();
+            private boolean mTestLogin;
+            private int mLogLevel;
+
+            Builder(String rootDir, String cloudName, String cloudAdmin) {
+                mUsesCloud = true;
+                this.mRootDir = rootDir;
+                this.mCloudName = cloudName;
+                this.mCloudAdmin = cloudAdmin;
+            }
+
+            Builder(String rootDir) {
+                mUsesCloud = false;
+                mRootDir = rootDir;
+                mCloudName = null;
+                mCloudAdmin = null;
+            }
+
+            /**
+             * Sets the executor where callbacks will run (e.g., watch, invite, login, etc.).
+             * The default executor is the UI Thread.
+             *
+             * @param executor Callback executor
+             */
+            public Builder setExecutor(Executor executor) {
+                mExecutor = executor;
+                return this;
+            }
+
+            /**
+             * Used for tests. The app name is set to 'app', the user name is set to 'user' and the
+             * arguments to login() are ignored.
+             */
+            public Builder withTestLogin() {
+                mTestLogin = true;
+                return this;
+            }
+
+            /**
+             * Sets a single location for Syncbase peers to meet if internet is available.
+             *
+             * @param mountPoint Location to meet for syncing purposes
+             */
+            public Builder setMountPoint(String mountPoint) {
+                mMountPoints.clear();
+                mMountPoints.add(mountPoint);
+                return this;
+            }
+
+            /**
+             * Sets a list of locations for Syncbase peers to meet if internet is available.
+             *
+             * @param mountPoints Locations to meet for syncing purposes
+             */
+            public Builder setMountPoints(java.util.Collection<String> mountPoints) {
+                mMountPoints.clear();
+                mMountPoints.addAll(mountPoints);
+                return this;
+            }
+
+            /**
+             * Used for debugging. Defaults to 0 (no logging). When >0, Syncbase logs will be sent
+             * to stdout, with higher log levels logging more data.
+             *
+             * @param logLevel Syncbase log level
+             */
+            public Builder setLogLevel(int logLevel) {
+                mLogLevel = logLevel;
+                return this;
+            }
+
+            /**
+             * Builds the Syncbase Options.
+             */
+            public Options build() {
+                return new Options(this);
+            }
         }
     }
 
@@ -107,9 +210,9 @@
 
     static Options sOpts;
     private static Database sDatabase;
-    static Collection sUserdataCollection;
     private static final Object sScanMappingMu = new Object();
     private static final Map<ScanNeighborhoodForUsersCallback, Long> sScanMapping = new HashMap<>();
+    private static String sAppBlessing;
 
     // TODO(sadovsky): Maybe set DB_NAME to "db__" so that it is less likely to collide with
     // developer-specified names.
@@ -124,8 +227,10 @@
             USERDATA_COLLECTION_PREFIX = "__collections/";
 
     private static Map selfAndCloud() throws SyncbaseException {
-        return ImmutableMap.of(Permissions.IN,
-                ImmutableList.of(getPersonalBlessingString(), sOpts.getCloudBlessingString()));
+        List<String> inList = sOpts.mCloudAdmin == null
+                ? ImmutableList.of(getPersonalBlessingString())
+                : ImmutableList.of(getPersonalBlessingString(), sOpts.mCloudAdmin);
+        return ImmutableMap.of(Permissions.IN, inList);
     }
 
     /**
@@ -137,7 +242,7 @@
         try {
             System.loadLibrary("syncbase");
             sOpts = opts;
-            io.v.syncbase.internal.Service.Init(sOpts.rootDir, sOpts.testLogin);
+            io.v.syncbase.internal.Service.Init(sOpts.mRootDir, sOpts.mTestLogin, sOpts.mLogLevel);
             if (isLoggedIn()) {
                 io.v.syncbase.internal.Service.Serve();
             }
@@ -155,10 +260,16 @@
             if (!isLoggedIn()) {
                 return null;
             }
+            if (sAppBlessing == null) {
+                // Set the app blessing at this stage for later use.
+                // Should not error because the user is logged in.
+                sAppBlessing = io.v.syncbase.internal.Blessings.AppBlessingFromContext();
+            }
             if (sDatabase != null) {
                 // TODO(sadovsky): Check that opts matches original opts (sOpts)?
                 return sDatabase;
             }
+
             sDatabase = new Database(Service.database(DB_NAME));
             return sDatabase;
 
@@ -201,8 +312,9 @@
 
     /**
      * Logs in the user on Android.
-     * If the user is already logged in, it runs the callback immediately. Otherwise, the user
-     * selects an account through an account picker flow and is logged into Syncbase.
+     * If the user is already logged in, it runs the success callback on the executor. Otherwise,
+     * the user selects an account through an account picker flow and is logged into Syncbase. The
+     * callback's success or failure cases are called accordingly.
      * Note: This default account flow is currently restricted to Google accounts.
      *
      * @param activity The Android activity where login will occur.
@@ -210,7 +322,12 @@
      */
     public static void loginAndroid(Activity activity, final LoginCallback cb) {
         if (isLoggedIn()) {
-            cb.onSuccess();
+            sOpts.mCallbackExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    cb.onSuccess();
+                }
+            });
             return;
         }
         FragmentTransaction transaction = activity.getFragmentManager().beginTransaction();
@@ -257,25 +374,25 @@
                         return;
                     }
                     sDatabase.createIfMissing();
-                    sUserdataCollection = sDatabase.createNamedCollection(
+                    Collection userdata = sDatabase.createNamedCollection(
                             USERDATA_NAME,
                             new DatabaseHandle.CollectionOptions().setWithoutSyncgroup(true));
-                    if (!sOpts.disableUserdataSyncgroup) {
-                        Syncgroup syncgroup = sUserdataCollection.getSyncgroup();
+                    if (!sOpts.mDisableUserdataSyncgroup) {
+                        Syncgroup syncgroup = userdata.getSyncgroup();
                         // Join-Or-Create pattern. If join fails, create the syncgroup instead.
                         // Note: Syncgroup merge does not exist yet, so this may potentially lead
                         // to split-brain syncgroups. This is exacerbated by lack of cloud instance.
                         try {
                             syncgroup.join();
                         } catch(VError e) {
-                            syncgroup.createIfMissing(ImmutableList.of(sUserdataCollection));
+                            syncgroup.createIfMissing(ImmutableList.of(userdata));
                         }
                         Database.AddWatchChangeHandlerOptions opts = new Database
                                 .AddWatchChangeHandlerOptions.Builder().
                                 setShowUserdataCollectionRow(true).build();
                         sDatabase.addWatchChangeHandler(new UserdataWatchHandler(), opts);
                     }
-                    sOpts.callbackExecutor.execute(new Runnable() {
+                    sOpts.mCallbackExecutor.execute(new Runnable() {
                         @Override
                         public void run() {
                             cb.onSuccess();
@@ -325,7 +442,8 @@
     }
 
     static void addToUserdata(Id id) throws SyncbaseException {
-        sUserdataCollection.put(Syncbase.USERDATA_COLLECTION_PREFIX + id.encode(), true);
+        sDatabase.getUserdataCollection().
+                put(Syncbase.USERDATA_COLLECTION_PREFIX + id.encode(), true);
     }
 
     /**
@@ -340,7 +458,7 @@
                     @Override
                     public void onPeer(final NeighborhoodPeer peer) {
                         final SettableFuture<Boolean> setFuture = SettableFuture.create();
-                        Syncbase.sOpts.callbackExecutor.execute(new Runnable() {
+                        Syncbase.sOpts.mCallbackExecutor.execute(new Runnable() {
                             @Override
                             public void run() {
                                 User u = new User(getAliasFromBlessingPattern(peer.blessings));
@@ -434,7 +552,7 @@
     }
 
     protected static String getBlessingStringFromAlias(String alias) {
-        return sOpts.defaultBlessingStringPrefix + alias;
+        return sAppBlessing + ":" + alias;
     }
 
     protected static String getAliasFromBlessingPattern(String blessingStr) {
diff --git a/syncbase/src/main/java/io/v/syncbase/Syncgroup.java b/syncbase/src/main/java/io/v/syncbase/Syncgroup.java
index ec0415e..7085086 100644
--- a/syncbase/src/main/java/io/v/syncbase/Syncgroup.java
+++ b/syncbase/src/main/java/io/v/syncbase/Syncgroup.java
@@ -38,10 +38,10 @@
         }
 
         SyncgroupSpec spec = new SyncgroupSpec();
-        spec.publishSyncbaseName = Syncbase.sOpts.getPublishSyncbaseName();
+        spec.publishSyncbaseName = Syncbase.sOpts.mCloudName;
         spec.permissions = Syncbase.defaultSyncgroupPerms();
         spec.collections = ids;
-        spec.mountTables = Syncbase.sOpts.mountPoints;
+        spec.mountTables = Syncbase.sOpts.mMountPoints;
         spec.isPrivate = false;
 
         try {
@@ -62,7 +62,8 @@
     protected void join() throws VError {
         // TODO(razvanm): Find a way to restrict the remote blessing. Cloud is one thing the remote
         // blessings should include.
-        mCoreSyncgroup.join("", ImmutableList.of("..."), new SyncgroupMemberInfo());
+        mCoreSyncgroup.join(Syncbase.sOpts.mCloudName, ImmutableList.of("..."),
+                new SyncgroupMemberInfo());
     }
 
     /**
diff --git a/syncbase/src/main/java/io/v/syncbase/internal/Service.java b/syncbase/src/main/java/io/v/syncbase/internal/Service.java
index 4ac2634..b2bd43e 100644
--- a/syncbase/src/main/java/io/v/syncbase/internal/Service.java
+++ b/syncbase/src/main/java/io/v/syncbase/internal/Service.java
@@ -11,7 +11,7 @@
 import io.v.syncbase.core.VersionedPermissions;
 
 public class Service {
-    public static native void Init(String rootDir, boolean testLogin);
+    public static native void Init(String rootDir, boolean testLogin, int logLevel);
     public static native void Serve() throws VError;
     public static native void Shutdown();
 
diff --git a/syncbase/src/test/java/io/v/syncbase/SyncbaseTest.java b/syncbase/src/test/java/io/v/syncbase/SyncbaseTest.java
index 8513424..5c53115 100644
--- a/syncbase/src/test/java/io/v/syncbase/SyncbaseTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/SyncbaseTest.java
@@ -17,7 +17,6 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
-import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 
 import io.v.syncbase.core.Permissions;
diff --git a/syncbase/src/test/java/io/v/syncbase/TestUtil.java b/syncbase/src/test/java/io/v/syncbase/TestUtil.java
index 37ec0ce..2019b05 100644
--- a/syncbase/src/test/java/io/v/syncbase/TestUtil.java
+++ b/syncbase/src/test/java/io/v/syncbase/TestUtil.java
@@ -41,12 +41,8 @@
 
     static void setUpSyncbase(File folder) throws SyncbaseException, ExecutionException,
             InterruptedException {
-        Syncbase.Options opts = new Syncbase.Options();
-        opts.rootDir = folder.getAbsolutePath();
-        opts.disableUserdataSyncgroup = true;
-        opts.disableSyncgroupPublishing = true;
-        opts.testLogin = true;
-        opts.callbackExecutor = sameThreadExecutor;
+        Syncbase.Options opts = Syncbase.Options.offlineBuilder(folder.getAbsolutePath()).
+                withTestLogin().setExecutor(sameThreadExecutor).build();
         Syncbase.init(opts);
     }
 
diff --git a/syncbase/src/test/java/io/v/syncbase/core/BatchDatabaseTest.java b/syncbase/src/test/java/io/v/syncbase/core/BatchDatabaseTest.java
index d026ad3..8c4407c 100644
--- a/syncbase/src/test/java/io/v/syncbase/core/BatchDatabaseTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/core/BatchDatabaseTest.java
@@ -21,7 +21,7 @@
     @BeforeClass
     public static void setUp() throws Exception {
         System.loadLibrary("syncbase");
-        io.v.syncbase.internal.Service.Init(folder.newFolder().getAbsolutePath(), true);
+        io.v.syncbase.internal.Service.Init(folder.newFolder().getAbsolutePath(), true, 0);
         io.v.syncbase.internal.Service.Login("", "");
     }
 
diff --git a/syncbase/src/test/java/io/v/syncbase/core/CollectionTest.java b/syncbase/src/test/java/io/v/syncbase/core/CollectionTest.java
index babecd4..bebd252 100644
--- a/syncbase/src/test/java/io/v/syncbase/core/CollectionTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/core/CollectionTest.java
@@ -27,7 +27,7 @@
     @BeforeClass
     public static void setUp() throws Exception {
         System.loadLibrary("syncbase");
-        io.v.syncbase.internal.Service.Init(folder.newFolder().getAbsolutePath(), true);
+        io.v.syncbase.internal.Service.Init(folder.newFolder().getAbsolutePath(), true, 0);
         io.v.syncbase.internal.Service.Login("", "");
     }
 
diff --git a/syncbase/src/test/java/io/v/syncbase/core/DatabaseHandleTest.java b/syncbase/src/test/java/io/v/syncbase/core/DatabaseHandleTest.java
index 7b94b7b..e2a0760 100644
--- a/syncbase/src/test/java/io/v/syncbase/core/DatabaseHandleTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/core/DatabaseHandleTest.java
@@ -25,7 +25,7 @@
     @BeforeClass
     public static void setUp() throws Exception {
         System.loadLibrary("syncbase");
-        io.v.syncbase.internal.Service.Init(folder.newFolder().getAbsolutePath(), true);
+        io.v.syncbase.internal.Service.Init(folder.newFolder().getAbsolutePath(), true, 0);
         io.v.syncbase.internal.Service.Login("", "");
     }
 
diff --git a/syncbase/src/test/java/io/v/syncbase/core/DatabaseTest.java b/syncbase/src/test/java/io/v/syncbase/core/DatabaseTest.java
index 9bf9139..7c207df 100644
--- a/syncbase/src/test/java/io/v/syncbase/core/DatabaseTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/core/DatabaseTest.java
@@ -27,7 +27,7 @@
     @BeforeClass
     public static void setUp() throws Exception {
         System.loadLibrary("syncbase");
-        io.v.syncbase.internal.Service.Init(folder.newFolder().getAbsolutePath(), true);
+        io.v.syncbase.internal.Service.Init(folder.newFolder().getAbsolutePath(), true, 0);
         io.v.syncbase.internal.Service.Login("", "");
     }
 
diff --git a/syncbase/src/test/java/io/v/syncbase/core/RowTest.java b/syncbase/src/test/java/io/v/syncbase/core/RowTest.java
index 4e6ff05..fb291aa 100644
--- a/syncbase/src/test/java/io/v/syncbase/core/RowTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/core/RowTest.java
@@ -24,7 +24,7 @@
     @BeforeClass
     public static void setUp() throws Exception {
         System.loadLibrary("syncbase");
-        io.v.syncbase.internal.Service.Init(folder.newFolder().getAbsolutePath(), true);
+        io.v.syncbase.internal.Service.Init(folder.newFolder().getAbsolutePath(), true, 0);
         io.v.syncbase.internal.Service.Login("", "");
     }
 
diff --git a/syncbase/src/test/java/io/v/syncbase/core/ServiceTest.java b/syncbase/src/test/java/io/v/syncbase/core/ServiceTest.java
index 1ebabd3..eceda91 100644
--- a/syncbase/src/test/java/io/v/syncbase/core/ServiceTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/core/ServiceTest.java
@@ -23,7 +23,7 @@
     @BeforeClass
     public static void setUp() throws Exception {
         System.loadLibrary("syncbase");
-        io.v.syncbase.internal.Service.Init(folder.newFolder().getAbsolutePath(), true);
+        io.v.syncbase.internal.Service.Init(folder.newFolder().getAbsolutePath(), true, 0);
         io.v.syncbase.internal.Service.Login("", "");
     }
 
diff --git a/syncbase/src/test/java/io/v/syncbase/core/SyncgroupTest.java b/syncbase/src/test/java/io/v/syncbase/core/SyncgroupTest.java
index 38aec09..87a7e37 100644
--- a/syncbase/src/test/java/io/v/syncbase/core/SyncgroupTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/core/SyncgroupTest.java
@@ -30,7 +30,7 @@
     @BeforeClass
     public static void setUp() throws Exception {
         System.loadLibrary("syncbase");
-        io.v.syncbase.internal.Service.Init(folder.newFolder().getAbsolutePath(), true);
+        io.v.syncbase.internal.Service.Init(folder.newFolder().getAbsolutePath(), true, 0);
         io.v.syncbase.internal.Service.Login("", "");
     }
 
diff --git a/syncbase/src/test/java/io/v/syncbase/internal/BlessingsTest.java b/syncbase/src/test/java/io/v/syncbase/internal/BlessingsTest.java
index d9eab60..95bc990 100644
--- a/syncbase/src/test/java/io/v/syncbase/internal/BlessingsTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/internal/BlessingsTest.java
@@ -24,7 +24,7 @@
     @BeforeClass
     public static void setUp() throws Exception {
         System.loadLibrary("syncbase");
-        Service.Init(folder.newFolder().getAbsolutePath(), true);
+        Service.Init(folder.newFolder().getAbsolutePath(), true, 0);
         Service.Login("", "");
     }
 
diff --git a/syncbase/src/test/java/io/v/syncbase/internal/CollectionTest.java b/syncbase/src/test/java/io/v/syncbase/internal/CollectionTest.java
index 9fd1d7e..fb257f3 100644
--- a/syncbase/src/test/java/io/v/syncbase/internal/CollectionTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/internal/CollectionTest.java
@@ -37,7 +37,7 @@
     @BeforeClass
     public static void setUp() throws Exception {
         System.loadLibrary("syncbase");
-        Service.Init(folder.newFolder().getAbsolutePath(), true);
+        Service.Init(folder.newFolder().getAbsolutePath(), true, 0);
         Service.Login("", "");
     }
 
diff --git a/syncbase/src/test/java/io/v/syncbase/internal/DatabaseTest.java b/syncbase/src/test/java/io/v/syncbase/internal/DatabaseTest.java
index 0139508..dfc5b63 100644
--- a/syncbase/src/test/java/io/v/syncbase/internal/DatabaseTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/internal/DatabaseTest.java
@@ -47,7 +47,7 @@
     @BeforeClass
     public static void setUp() throws Exception {
         System.loadLibrary("syncbase");
-        Service.Init(folder.newFolder().getAbsolutePath(), true);
+        Service.Init(folder.newFolder().getAbsolutePath(), true, 0);
         Service.Login("", "");
     }
 
diff --git a/syncbase/src/test/java/io/v/syncbase/internal/RowTest.java b/syncbase/src/test/java/io/v/syncbase/internal/RowTest.java
index 5bc7488..725dd83 100644
--- a/syncbase/src/test/java/io/v/syncbase/internal/RowTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/internal/RowTest.java
@@ -29,7 +29,7 @@
     @BeforeClass
     public static void setUp() throws Exception {
         System.loadLibrary("syncbase");
-        Service.Init(folder.newFolder().getAbsolutePath(), true);
+        Service.Init(folder.newFolder().getAbsolutePath(), true, 0);
         Service.Login("", "");
     }
 
diff --git a/syncbase/src/test/java/io/v/syncbase/internal/ServiceTest.java b/syncbase/src/test/java/io/v/syncbase/internal/ServiceTest.java
index 2799fde..6a4b9f6 100644
--- a/syncbase/src/test/java/io/v/syncbase/internal/ServiceTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/internal/ServiceTest.java
@@ -25,7 +25,7 @@
     @BeforeClass
     public static void setUp() throws Exception {
         System.loadLibrary("syncbase");
-        Service.Init(folder.newFolder().getAbsolutePath(), true);
+        Service.Init(folder.newFolder().getAbsolutePath(), true, 0);
         Service.Login("", "");
     }