SyncedFolderObserverService.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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.Context;
  26. import android.content.Intent;
  27. import android.os.Binder;
  28. import android.os.IBinder;
  29. import android.support.v4.util.Pair;
  30. import com.owncloud.android.MainApp;
  31. import com.owncloud.android.datamodel.SyncedFolder;
  32. import com.owncloud.android.datamodel.SyncedFolderProvider;
  33. import com.owncloud.android.lib.common.utils.Log_OC;
  34. import com.owncloud.android.services.FileAlterationMagicListener;
  35. import org.apache.commons.io.FileUtils;
  36. import org.apache.commons.io.monitor.FileAlterationMonitor;
  37. import org.apache.commons.io.monitor.FileEntry;
  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<Pair<SyncedFolder, FileEntry>> pairArrayList = new CopyOnWriteArrayList<>();
  56. private File file;
  57. @Override
  58. public void onCreate() {
  59. mProvider = new SyncedFolderProvider(MainApp.getAppContext().getContentResolver());
  60. }
  61. @Override
  62. public int onStartCommand(Intent intent, int flags, int startId) {
  63. monitor = new FileAlterationMonitor();
  64. fileFilter = new FileFilter() {
  65. @Override
  66. public boolean accept(File pathname) {
  67. return !pathname.getName().startsWith(".") && !pathname.getName().endsWith(".tmp");
  68. }
  69. };
  70. file = new File(MainApp.getAppContext().getExternalFilesDir(null).getAbsolutePath() + "/nc_persistence");
  71. if (file.exists() ) {
  72. FileInputStream fis = null;
  73. try {
  74. fis = new FileInputStream(file);
  75. ObjectInputStream ois = new ObjectInputStream(fis);
  76. pairArrayList = (CopyOnWriteArrayList<Pair<SyncedFolder, FileEntry>>)ois.readObject();
  77. } catch (FileNotFoundException e) {
  78. Log_OC.d(TAG, "Failed with FileNotFound while reading persistence file");
  79. } catch (IOException e) {
  80. Log_OC.d(TAG, "Failed with IOException while reading persistence file");
  81. } catch (ClassNotFoundException e) {
  82. Log_OC.d(TAG, "Failed with ClassNotFound while reading persistence file");
  83. } finally {
  84. try {
  85. if (fis != null) {
  86. fis.close();
  87. }
  88. } catch (IOException e) {
  89. Log_OC.d(TAG, "Failed with closing FIS");
  90. }
  91. }
  92. }
  93. Log_OC.d(TAG, "start");
  94. if (pairArrayList.size() == 0) {
  95. for (SyncedFolder syncedFolder : mProvider.getSyncedFolders()) {
  96. if (syncedFolder.isEnabled() && !syncedFolderMap.containsKey(syncedFolder.getLocalPath())) {
  97. Log_OC.d(TAG, "start observer: " + syncedFolder.getLocalPath());
  98. FileAlterationMagicObserver observer = new FileAlterationMagicObserver(new File(
  99. syncedFolder.getLocalPath()), fileFilter);
  100. try {
  101. observer.init();
  102. Pair<SyncedFolder, FileEntry> pair = new Pair<>(syncedFolder, observer.getRootEntry());
  103. pairArrayList.add(pair);
  104. } catch (Exception e) {
  105. Log_OC.d(TAG, "Failed getting an observer to intialize");
  106. }
  107. observer.addListener(new FileAlterationMagicListener(syncedFolder));
  108. monitor.addObserver(observer);
  109. syncedFolderMap.put(syncedFolder, observer);
  110. }
  111. }
  112. } else {
  113. for(int i = 0; i < pairArrayList.size(); i++) {
  114. SyncedFolder syncFolder = pairArrayList.get(i).first;
  115. FileAlterationMagicObserver observer = new FileAlterationMagicObserver(new File(
  116. syncFolder.getLocalPath()), fileFilter);
  117. observer.setRootEntry(pairArrayList.get(i).second);
  118. observer.addListener(new FileAlterationMagicListener(syncFolder));
  119. monitor.addObserver(observer);
  120. syncedFolderMap.put(syncFolder, observer);
  121. }
  122. }
  123. writePersistenceEntries(file);
  124. try {
  125. monitor.start();
  126. } catch (Exception e) {
  127. Log_OC.d(TAG, "Something went very wrong at onStartCommand");
  128. }
  129. return Service.START_NOT_STICKY;
  130. }
  131. private void writePersistenceEntries(File file) {
  132. FileOutputStream fos = null;
  133. try {
  134. if (pairArrayList.size() > 0) {
  135. fos = MainApp.getAppContext().openFileOutput(file.getAbsolutePath(), Context.MODE_PRIVATE);
  136. ObjectOutputStream os = new ObjectOutputStream(fos);
  137. for (int i = 0; i < pairArrayList.size(); i++) {
  138. os.writeObject(pairArrayList.get(i));
  139. }
  140. os.close();
  141. } else if (file.exists() && pairArrayList.size() == 0) {
  142. FileUtils.deleteQuietly(file);
  143. }
  144. if (fos != null) {
  145. fos.close();
  146. }
  147. } catch (FileNotFoundException e) {
  148. Log_OC.d(TAG, "Failed writing to nc_sync_persistance file via FileNotFound");
  149. } catch (IOException e) {
  150. Log_OC.d(TAG, "Failed writing to nc_sync_persistance file via IOException");
  151. }
  152. }
  153. @Override
  154. public void onDestroy() {
  155. for (SyncedFolder syncedFolder : syncedFolderMap.keySet()) {
  156. FileAlterationMagicObserver obs = syncedFolderMap.get(syncedFolder);
  157. for (int i = 0; i < pairArrayList.size(); i++) {
  158. SyncedFolder pairSyncedFolder = pairArrayList.get(i).first;
  159. if (pairSyncedFolder.equals(syncedFolder)) {
  160. Pair<SyncedFolder, FileEntry> newPairEntry = new Pair<>(syncedFolder, obs.getRootEntry());
  161. pairArrayList.set(i, newPairEntry);
  162. break;
  163. }
  164. }
  165. monitor.removeObserver(obs);
  166. syncedFolderMap.remove(obs);
  167. try {
  168. obs.destroy();
  169. } catch (Exception e) {
  170. Log_OC.d(TAG, "Something went very wrong at onDestroy");
  171. }
  172. }
  173. writePersistenceEntries(file);
  174. }
  175. /**
  176. * Restart oberver if it is enabled
  177. * If syncedFolder exists already, use it, otherwise create new observer
  178. *
  179. * @param syncedFolder
  180. */
  181. public void restartObserver(SyncedFolder syncedFolder) {
  182. FileAlterationMagicObserver fileAlterationObserver;
  183. if (syncedFolderMap.containsKey(syncedFolder)) {
  184. Log_OC.d(TAG, "stop observer: " + syncedFolder.getLocalPath());
  185. fileAlterationObserver = syncedFolderMap.get(syncedFolder);
  186. monitor.removeObserver(fileAlterationObserver);
  187. try {
  188. fileAlterationObserver.destroy();
  189. } catch (Exception e) {
  190. Log_OC.d(TAG, "Something went very wrong at onDestroy");
  191. }
  192. // remove it from the paired array list
  193. for (int i = 0; i < pairArrayList.size(); i++) {
  194. if (syncedFolder.equals(pairArrayList.get(i).first)) {
  195. pairArrayList.remove(i);
  196. break;
  197. }
  198. }
  199. syncedFolderMap.remove(syncedFolder);
  200. }
  201. if (syncedFolder.isEnabled()) {
  202. Log_OC.d(TAG, "start observer: " + syncedFolder.getLocalPath());
  203. if (syncedFolderMap.containsKey(syncedFolder)) {
  204. fileAlterationObserver = syncedFolderMap.get(syncedFolder);
  205. if (fileAlterationObserver.getListeners() == null) {
  206. fileAlterationObserver.addListener(new FileAlterationMagicListener(syncedFolder));
  207. }
  208. monitor.addObserver(fileAlterationObserver);
  209. } else {
  210. fileAlterationObserver = new FileAlterationMagicObserver(new File(syncedFolder.getLocalPath()),
  211. fileFilter);
  212. try {
  213. fileAlterationObserver.init();
  214. Pair<SyncedFolder, FileEntry> pair = new Pair<>(syncedFolder, fileAlterationObserver.getRootEntry());
  215. pairArrayList.add(pair);
  216. } catch (Exception e) {
  217. Log_OC.d(TAG, "Failed getting an observer to intialize");
  218. }
  219. fileAlterationObserver.addListener(new FileAlterationMagicListener(syncedFolder));
  220. monitor.addObserver(fileAlterationObserver);
  221. try {
  222. syncedFolderMap.put(syncedFolder, fileAlterationObserver);
  223. } catch (Exception e) {
  224. Log_OC.d(TAG, "Something went very wrong on RestartObserver");
  225. }
  226. }
  227. }
  228. writePersistenceEntries(file);
  229. }
  230. @Override
  231. public IBinder onBind(Intent arg0) {
  232. return mBinder;
  233. }
  234. public class SyncedFolderObserverBinder extends Binder {
  235. public SyncedFolderObserverService getService() {
  236. return SyncedFolderObserverService.this;
  237. }
  238. }
  239. }