SyncedFolderObserverService.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. /**
  2. * Nextcloud Android client application
  3. *
  4. * @author Tobias Kaminsky
  5. * @author Andy Scherzinger
  6. * @author Mario Danic
  7. * Copyright (C) 2016 Tobias Kaminsky, Andy Scherzinger
  8. * Copyright (C) 2017 Mario Danic
  9. * <p>
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Affero General Public License as published by
  12. * the Free Software Foundation, either version 3 of the License, or
  13. * at your option) any later version.
  14. * <p>
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Affero General Public License for more details.
  19. * <p>
  20. * You should have received a copy of the GNU Affero General Public License
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. */
  23. package com.owncloud.android.services.observer;
  24. import android.app.Service;
  25. import android.content.Intent;
  26. import android.os.Binder;
  27. import android.os.IBinder;
  28. import com.owncloud.android.MainApp;
  29. import com.owncloud.android.datamodel.SerializablePair;
  30. import com.owncloud.android.datamodel.SyncedFolder;
  31. import com.owncloud.android.datamodel.SyncedFolderProvider;
  32. import com.owncloud.android.lib.common.utils.Log_OC;
  33. import com.owncloud.android.services.FileAlterationMagicListener;
  34. import org.apache.commons.io.FileUtils;
  35. import org.apache.commons.io.monitor.FileAlterationMonitor;
  36. import org.apache.commons.io.monitor.FileEntry;
  37. import java.io.EOFException;
  38. import java.io.File;
  39. import java.io.FileFilter;
  40. import java.io.FileInputStream;
  41. import java.io.FileNotFoundException;
  42. import java.io.FileOutputStream;
  43. import java.io.IOException;
  44. import java.io.ObjectInputStream;
  45. import java.io.ObjectOutputStream;
  46. import java.util.HashMap;
  47. import java.util.concurrent.CopyOnWriteArrayList;
  48. public class SyncedFolderObserverService extends Service {
  49. private static final String TAG = "SyncedFolderObserverService";
  50. private SyncedFolderProvider mProvider;
  51. private HashMap<SyncedFolder, FileAlterationMagicObserver> syncedFolderMap = new HashMap<>();
  52. private final IBinder mBinder = new SyncedFolderObserverBinder();
  53. private FileAlterationMonitor monitor;
  54. private FileFilter fileFilter;
  55. private CopyOnWriteArrayList<SerializablePair<SyncedFolder, FileEntry>> pairArrayList = new CopyOnWriteArrayList<>();
  56. private File file;
  57. @Override
  58. public void onCreate() {
  59. mProvider = new SyncedFolderProvider(MainApp.getAppContext().getContentResolver());
  60. monitor = new FileAlterationMonitor();
  61. fileFilter = new FileFilter() {
  62. @Override
  63. public boolean accept(File pathname) {
  64. return !pathname.getName().startsWith(".") && !pathname.getName().endsWith(".tmp");
  65. }
  66. };
  67. file = new File(MainApp.getAppContext().getExternalFilesDir(null).getAbsolutePath() + File.separator +
  68. "nc_persistence");
  69. boolean readPerstistanceEntries = false;
  70. if (file.exists()) {
  71. FileInputStream fis = null;
  72. try {
  73. fis = new FileInputStream(file);
  74. ObjectInputStream ois = new ObjectInputStream(fis);
  75. boolean cont = true;
  76. while (cont) {
  77. Object obj = ois.readObject();
  78. if (obj != null)
  79. pairArrayList.add((SerializablePair<SyncedFolder, FileEntry>) obj);
  80. else
  81. cont = false;
  82. }
  83. readPerstistanceEntries = true;
  84. } catch (FileNotFoundException e) {
  85. Log_OC.d(TAG, "Failed with FileNotFound while reading persistence file");
  86. } catch (EOFException e) {
  87. Log_OC.d(TAG, "Failed with EOFException while reading persistence file");
  88. readPerstistanceEntries = true;
  89. } catch (IOException e) {
  90. Log_OC.d(TAG, "Failed with IOException while reading persistence file");
  91. } catch (ClassNotFoundException e) {
  92. Log_OC.d(TAG, "Failed with ClassNotFound while reading persistence file");
  93. } finally {
  94. try {
  95. if (fis != null) {
  96. fis.close();
  97. }
  98. } catch (IOException e) {
  99. Log_OC.d(TAG, "Failed with closing FIS");
  100. }
  101. }
  102. }
  103. Log_OC.d(TAG, "start");
  104. if (pairArrayList.size() == 0) {
  105. for (SyncedFolder syncedFolder : mProvider.getSyncedFolders()) {
  106. if (syncedFolder.isEnabled() && !syncedFolderMap.containsKey(syncedFolder)) {
  107. Log_OC.d(TAG, "start observer: " + syncedFolder.getLocalPath());
  108. FileAlterationMagicObserver observer = new FileAlterationMagicObserver(new File(
  109. syncedFolder.getLocalPath()), fileFilter);
  110. try {
  111. observer.init();
  112. SerializablePair<SyncedFolder, FileEntry> pair = new SerializablePair<>(syncedFolder,
  113. observer.getRootEntry());
  114. pairArrayList.add(pair);
  115. } catch (Exception e) {
  116. Log_OC.d(TAG, "Failed getting an observer to intialize");
  117. }
  118. observer.addListener(new FileAlterationMagicListener(syncedFolder));
  119. monitor.addObserver(observer);
  120. syncedFolderMap.put(syncedFolder, observer);
  121. }
  122. }
  123. } else {
  124. for (int i = 0; i < pairArrayList.size(); i++) {
  125. SyncedFolder syncFolder = pairArrayList.get(i).getKey();
  126. for (SyncedFolder syncedFolder : mProvider.getSyncedFolders()) {
  127. if (syncFolder.getId() == syncedFolder.getId()) {
  128. syncFolder = syncedFolder;
  129. pairArrayList.set(i, new SerializablePair<SyncedFolder, FileEntry>(syncFolder,
  130. pairArrayList.get(i).getValue()));
  131. break;
  132. }
  133. }
  134. FileAlterationMagicObserver observer = new FileAlterationMagicObserver(new File(
  135. syncFolder.getLocalPath()), fileFilter);
  136. observer.setRootEntry(pairArrayList.get(i).getValue());
  137. observer.addListener(new FileAlterationMagicListener(syncFolder));
  138. monitor.addObserver(observer);
  139. syncedFolderMap.put(syncFolder, observer);
  140. }
  141. }
  142. writePersistenceEntries(readPerstistanceEntries, file);
  143. try {
  144. monitor.start();
  145. } catch (Exception e) {
  146. Log_OC.d(TAG, "Something went very wrong at onStartCommand");
  147. }
  148. }
  149. @Override
  150. public int onStartCommand(Intent intent, int flags, int startId) {
  151. return Service.START_NOT_STICKY;
  152. }
  153. private void writePersistenceEntries(boolean readPerstistanceEntries, File file) {
  154. FileOutputStream fos = null;
  155. try {
  156. if (pairArrayList.size() > 0 && !readPerstistanceEntries) {
  157. File newFile = new File(file.getAbsolutePath());
  158. if (!newFile.exists()) {
  159. newFile.createNewFile();
  160. }
  161. fos = new FileOutputStream(new File(file.getAbsolutePath()), false);
  162. ObjectOutputStream os = new ObjectOutputStream(fos);
  163. for (int i = 0; i < pairArrayList.size(); i++) {
  164. os.writeObject(pairArrayList.get(i));
  165. }
  166. os.close();
  167. } else if (file.exists() && pairArrayList.size() == 0) {
  168. FileUtils.deleteQuietly(file);
  169. }
  170. if (fos != null) {
  171. fos.close();
  172. }
  173. } catch (FileNotFoundException e) {
  174. Log_OC.d(TAG, "Failed writing to nc_sync_persistance file via FileNotFound");
  175. } catch (IOException e) {
  176. Log_OC.d(TAG, "Failed writing to nc_sync_persistance file via IOException");
  177. }
  178. }
  179. public void syncToDisk(boolean destructive) {
  180. for (SyncedFolder syncedFolder : syncedFolderMap.keySet()) {
  181. FileAlterationMagicObserver obs = syncedFolderMap.get(syncedFolder);
  182. for (int i = 0; i < pairArrayList.size(); i++) {
  183. SyncedFolder pairSyncedFolder = pairArrayList.get(i).getKey();
  184. if (pairSyncedFolder.equals(syncedFolder)) {
  185. SerializablePair<SyncedFolder, FileEntry> newPairEntry = new SerializablePair<>(syncedFolder,
  186. obs.getRootEntry());
  187. pairArrayList.set(i, newPairEntry);
  188. break;
  189. }
  190. }
  191. if (destructive) {
  192. monitor.removeObserver(obs);
  193. syncedFolderMap.remove(obs);
  194. }
  195. try {
  196. obs.destroy();
  197. } catch (Exception e) {
  198. Log_OC.d(TAG, "Something went very wrong at onDestroy");
  199. }
  200. }
  201. if (destructive) {
  202. try {
  203. monitor.stop();
  204. } catch (Exception e) {
  205. Log_OC.d(TAG, "Something went very wrong at onDestroy");
  206. }
  207. }
  208. writePersistenceEntries(false, file);
  209. }
  210. @Override
  211. public void onDestroy() {
  212. syncToDisk(true);
  213. }
  214. /**
  215. * Restart oberver if it is enabled
  216. * If syncedFolder exists already, use it, otherwise create new observer
  217. *
  218. * @param syncedFolder
  219. */
  220. public void restartObserver(SyncedFolder syncedFolder) {
  221. FileAlterationMagicObserver fileAlterationObserver;
  222. Log_OC.d(TAG, "stop observer: " + syncedFolder.getLocalPath());
  223. SyncedFolder syncy = null;
  224. for (SyncedFolder syncyFolder : syncedFolderMap.keySet()) {
  225. if (syncyFolder.getId() == syncedFolder.getId()) {
  226. syncy = syncyFolder;
  227. break;
  228. }
  229. }
  230. if (syncedFolder.isEnabled()) {
  231. if (syncy != null) {
  232. Log_OC.d(TAG, "start observer Restart: " + syncedFolder.getLocalPath() + " " + syncedFolder.getAccount());
  233. fileAlterationObserver = syncedFolderMap.get(syncy);
  234. monitor.removeObserver(fileAlterationObserver);
  235. fileAlterationObserver.removeListener(null);
  236. fileAlterationObserver.addListener(new FileAlterationMagicListener(syncedFolder));
  237. syncedFolderMap.remove(syncy);
  238. syncedFolderMap.put(syncedFolder, fileAlterationObserver);
  239. monitor.addObserver(fileAlterationObserver);
  240. // remove it from the paired array list
  241. for (int i = 0; i < pairArrayList.size(); i++) {
  242. if (syncy.getId() == pairArrayList.get(i).getKey().getId()) {
  243. pairArrayList.remove(i);
  244. break;
  245. }
  246. }
  247. pairArrayList.add(new SerializablePair<SyncedFolder, FileEntry>(syncedFolder,
  248. fileAlterationObserver.getRootEntry()));
  249. } else {
  250. Log_OC.d(TAG, "start observer Restart noMap: " + syncedFolder.getLocalPath() + " " + syncedFolder.getAccount());
  251. fileAlterationObserver = new FileAlterationMagicObserver(new File(syncedFolder.getLocalPath()),
  252. fileFilter);
  253. try {
  254. fileAlterationObserver.init();
  255. SerializablePair<SyncedFolder, FileEntry> pair = new SerializablePair<>(syncedFolder,
  256. fileAlterationObserver.getRootEntry());
  257. pairArrayList.add(pair);
  258. } catch (Exception e) {
  259. Log_OC.d(TAG, "Failed getting an observer to intialize");
  260. }
  261. fileAlterationObserver.addListener(new FileAlterationMagicListener(syncedFolder));
  262. monitor.addObserver(fileAlterationObserver);
  263. try {
  264. syncedFolderMap.put(syncedFolder, fileAlterationObserver);
  265. } catch (Exception e) {
  266. Log_OC.d(TAG, "Something went very wrong on RestartObserver");
  267. }
  268. }
  269. } else {
  270. fileAlterationObserver = syncedFolderMap.get(syncy);
  271. monitor.removeObserver(fileAlterationObserver);
  272. try {
  273. fileAlterationObserver.destroy();
  274. } catch (Exception e) {
  275. Log_OC.d(TAG, "Something went very wrong at onDestroy");
  276. }
  277. // remove it from the paired array list
  278. for (int i = 0; i < pairArrayList.size(); i++) {
  279. if (syncy.getId() == pairArrayList.get(i).getKey().getId()) {
  280. pairArrayList.remove(i);
  281. break;
  282. }
  283. }
  284. syncedFolderMap.remove(syncy);
  285. }
  286. writePersistenceEntries(false, file);
  287. }
  288. @Override
  289. public IBinder onBind(Intent arg0) {
  290. return mBinder;
  291. }
  292. public class SyncedFolderObserverBinder extends Binder {
  293. public SyncedFolderObserverService getService() {
  294. return SyncedFolderObserverService.this;
  295. }
  296. }
  297. }