2015년 7월 25일 토요일

Workaround for sync cycle problem between Mnemosyne Android app and Mnemosyne 2.3.3 on local machine

I've been an avid user of Mnemosyne Spaced Repetition Software (SRS) for several years now, and Peter Bienstman's recent development of a free Mnemosyne app for Android has made this software much more useful. I can now do card reviews during my daily subway commute without needing to be in front of a computer.

The Android app still has some rough edges, however. I store my Mnemosyne default.db on Dropbox, and do my card reviews on my work laptop, computers at home, as well as on my Android phone. In my experience, unfortunately, Mnemosyne Android only reliably syncs with the first machine it downloads cards from. So if I do an initial sync from my Dell Latitude notebook to my Android phone, I cannot sync Mnemosyne Android with Mnemosyne installed on my Lenovo laptop at work. If you try to do so, you will get an error message in the Android app stating:

Sync cycle detected. Sync through intermediate partner.

This is frustrating because during my morning and evening commute, I can easily do more than 100 card reviews total. I don't want to lose this record just because the in-app sync from Android to Mnemosyne desktop fails.

I found a variety of posts on the Internet about this issue, some of which advise extraneous steps. Through trial-and-error I found a simple workaround that just requires that you have adb installed on your Linux machine.

Solution: Overwrite default.db on your local machine with default.db from your Android phone

The path for default.db on your Android phone will be something like

/storage/sdcard0/Mnemosyne/default.db

Of course you should verify that this is the case by running adb shell and trying to navigate to /storage/sdcard0/Mnemosyne

adb is provided by the android-tools package on Archlinux. By default, adb has to be run as the root user, but this is not recommended due to security risks. Instead, use a Linux Access Control List (ACL) to allow your regular user to run /usr/bin/adb:

sudo setfacl -m "u:userName:rwx" /usr/bin/adb

This is better than just changing the file owner to some local user using chown or setting SUID on the file. (And much simpler than creating a udev rule just to connect to your phone using adb)
=============================================
Update 2016-10-8
When I wrote this post in July 2015 I initially recommended using ACL's to access your Android device from Linux. Now Archlinux has the package android-udev-rules which contains usb device ID's for popular Android phones. If your device ID isn't contained in the 51-android.rules udev file, simply git clone the android-udev-rules upstream repo on github, add your device ID to the rules file, and submit a pull request to the maintainer, M0Rf30.
=============================================
Connect your Android phone to your Linux machine via USB cable and then run adb devices to make sure the device is being properly detected by the Android Debug Bridge:

[archjun@latitude630 ~]$ adb devices
* daemon not running. starting it now on port 5037 *
* daemon started successfully *
List of devices attached 
12B9WQ2B1953661 device

Now run adb shell to find where default.db for Mnemosyne Android is located. Use ls -d */ to only show directories:

[archjun@latitude630 ~]$ adb shell
shell@android:/ $ ls -d */
acct/
cache/
config/
d/
data/
dev/
devlog/
etc/
firmware_q6/
firmware_radio/
mnt/
proc/
root/
sbin/
sdcard/
storage/
sys/
system/
tombstones/
usbdisk/
vendor/
shell@android:/ $ cd storage
shell@android:/storage $ ls -d */
sdcard0/
usbdisk/
shell@android:/storage $ cd sdcard0
shell@android:/storage/sdcard0 $ ls -d */
Alarms/
Android/
Books/
DCIM/
Download/
LOST.DIR/
Mnemosyne/
Movies/
Music/
My Documents/
Notifications/
Pictures/
Podcasts/
Ringtones/
TWRP/
clockworkmod/
data/

tmp/
shell@android:/storage/sdcard0 $ cd Mnemosyne
shell@android:/storage/sdcard0/Mnemosyne $ ls
backups
config.db
config.py
config.pyc
default.db
default.db-journal
default.db_media
history
machine.id
plugins
shell@android:/storage/sdcard0/Mnemosyne $ pwd

/storage/sdcard0/Mnemosyne

Voila! There's default.db! In another terminal window, navigate to the directory where default.db is stored on your local machine. In my case, I store all my Mnemosyne data in ~/Dropbox/mnemosyne, so I would cd into that directory, but YMMV. Now copy the output of pwd above which tells you the current working directory where your Android Mnemosyne file is located and run the following from your local machine's mnemosyne directory:

$ adb pull /storage/sdcard0/Mnemosyne/default.db

Note that you will not be prompted whether or not you want to overwrite the existing .db file on your local machine with the one from your Android phone, so be careful. It is not necessary to copy over other miscellaneous files like config.*, machine.id, etc.

Now if you launch Mnemosyne on your local machine, you will see that the card review data is identical to that of your Mnemosyne Android app. Once the Sync cycle detected error appears on your Mnemosyne Android app, you will probably have to delete all the Mnemosyne info from your phone's SD card and resync from your local machine. To delete the Mnemosyne directory from the sdcard, do the following (assuming your Mnemosyne Android path is /storage/sdcard0/Mnemosyne):

[archjun@latitude630 ~]$ adb shell
shell@android:/ $ rm -rf /storage/sdcard0/Mnemosyne

Now launch your Mnemosyne Android app, and press Sync while Mnemosyne is running on your Linux machine. Make sure that in Settings -> Configure Mnemosyne the Allow other devices to sync with this computer box is checked.

If you are on a distro that uses firewalld (like recent versions of Fedora), make sure that TCP port 8512 is open. A firewalld command that will open this port is the following:

firewall-cmd --add-port=8512/tcp

If you want to permanently add this port, add the option flag --permanent to the command above and then reload firewalld with firewall-cmd --reload